├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── bench ├── express.js ├── fastify.js ├── native.js ├── polka.js └── readme.md ├── examples ├── async-json │ ├── index.js │ ├── package.json │ └── readme.md ├── sub-app │ ├── index.js │ ├── package.json │ ├── readme.md │ └── users.js ├── with-afterjs │ ├── .gitignore │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ └── robots.txt │ ├── readme.md │ └── src │ │ ├── About.js │ │ ├── Home.css │ │ ├── Home.js │ │ ├── client.css │ │ ├── client.js │ │ ├── index.js │ │ ├── react.svg │ │ ├── routes.js │ │ └── server.js ├── with-apollo │ ├── index.js │ ├── package.json │ ├── readme.md │ └── screenshot.png ├── with-body-parser │ ├── index.js │ ├── package.json │ └── readme.md ├── with-firebase-admin │ ├── .gitignore │ ├── api │ │ ├── index.js │ │ ├── items.js │ │ └── services.js │ ├── client │ │ ├── .babelrc │ │ ├── index.html │ │ ├── index.js │ │ ├── package.json │ │ ├── tags │ │ │ ├── App.js │ │ │ ├── Footer.js │ │ │ ├── Item.js │ │ │ ├── Login.js │ │ │ └── TodoMVC.js │ │ └── utils │ │ │ ├── api.js │ │ │ └── local.js │ ├── index.js │ ├── package.json │ └── readme.md ├── with-graphql │ ├── index.js │ ├── package.json │ └── readme.md ├── with-https │ ├── index.js │ ├── package.json │ ├── readme.md │ └── ssl │ │ └── .gitkeep ├── with-morgan │ ├── index.js │ ├── items.js │ ├── package.json │ ├── readme.md │ └── util.js ├── with-nextjs │ ├── .gitignore │ ├── index.js │ ├── package.json │ ├── pages │ │ ├── about.js │ │ └── index.js │ └── readme.md ├── with-nuxtjs │ ├── .gitignore │ ├── index.js │ ├── package.json │ └── pages │ │ ├── about.vue │ │ └── index.vue ├── with-sapper │ ├── .gitignore │ ├── assets │ │ ├── favicon.png │ │ ├── global.css │ │ ├── great-success.png │ │ ├── manifest.json │ │ ├── svelte-logo-192.png │ │ └── svelte-logo-512.png │ ├── package.json │ ├── readme.md │ ├── routes │ │ ├── _components │ │ │ ├── Layout.html │ │ │ └── Nav.html │ │ ├── about.html │ │ ├── api │ │ │ └── blog │ │ │ │ ├── [slug].js │ │ │ │ ├── _posts.js │ │ │ │ └── index.js │ │ ├── blog │ │ │ ├── [slug].html │ │ │ └── index.html │ │ └── index.html │ ├── server.js │ ├── templates │ │ ├── 2xx.html │ │ ├── 4xx.html │ │ ├── 5xx.html │ │ ├── main.js │ │ └── service-worker.js │ ├── webpack.client.config.js │ └── webpack.server.config.js ├── with-serve-static │ ├── index.js │ ├── package.json │ ├── public │ │ ├── app.js │ │ ├── index.html │ │ └── style.css │ └── readme.md ├── with-server-sent-events │ ├── index.js │ ├── package.json │ ├── public │ │ └── index.html │ └── readme.md ├── with-sirv │ ├── index.js │ ├── package.json │ ├── public │ │ ├── app.js │ │ ├── index.html │ │ └── style.css │ └── readme.md ├── with-socketio │ ├── index.js │ ├── package.json │ ├── public │ │ ├── index.html │ │ ├── main.js │ │ └── style.css │ └── readme.md └── with-uws │ ├── index.js │ ├── package.json │ └── readme.md ├── lerna.json ├── license ├── package.json ├── packages ├── polka │ ├── index.js │ ├── package.json │ └── readme.md ├── send-type │ ├── index.js │ ├── package.json │ └── readme.md ├── send │ ├── index.js │ ├── package.json │ └── readme.md └── url │ ├── index.js │ ├── package.json │ └── readme.md ├── polka.png ├── readme.md └── tests ├── polka.js ├── send-type.js ├── send.js ├── url.js └── util ├── index.js └── mock.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_size = 2 6 | indent_style = tab 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.{json,yml,md}] 13 | indent_style = space 14 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: lukeed 2 | open_collective: polka 3 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | name: Node.js v${{ matrix.nodejs }} 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | nodejs: [8, 10, 12] 12 | 13 | steps: 14 | - uses: actions/checkout@master 15 | with: 16 | fetch-depth: 1 17 | - uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.nodejs }} 20 | 21 | - name: Install 22 | run: | 23 | npm install 24 | npm install -g nyc@13 25 | 26 | - name: Bootstrap, Lint, and Test 27 | run: nyc --exclude=tests npm test 28 | 29 | - name: Report 30 | if: matrix.nodejs >= 12 31 | run: | 32 | nyc report --reporter=text-lcov > coverage.lcov 33 | bash <(curl -s https://codecov.io/bash) 34 | env: 35 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | .nyc_output 4 | coverage 5 | *.lock 6 | *.log 7 | -------------------------------------------------------------------------------- /bench/express.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | function one(req, res, next) { 4 | req.one = true; 5 | next(); 6 | } 7 | 8 | function two(req, res, next) { 9 | req.two = true; 10 | next(); 11 | } 12 | 13 | express() 14 | .use(one, two) 15 | .get('/favicon.ico', _ => {}) 16 | .get('/', (req, res) => res.send('Hello')) 17 | .get('/user/:id', (req, res) => { 18 | res.end(`User: ${req.params.id}`); 19 | }) 20 | .listen(3000); 21 | -------------------------------------------------------------------------------- /bench/fastify.js: -------------------------------------------------------------------------------- 1 | const fastify = require('fastify'); 2 | 3 | function one(req, res, next) { 4 | req.one = true; 5 | next(); 6 | } 7 | 8 | function two(req, res, next) { 9 | req.two = true; 10 | next(); 11 | } 12 | 13 | fastify() 14 | .use(one) 15 | .use(two) 16 | .get('/favicon.ico', _ => {}) 17 | .get('/', (_, res) => res.send('Hello')) 18 | .get('/user/:id', (req, res) => { 19 | res.send(`User: ${req.params.id}`); 20 | }) 21 | .listen(3000); 22 | -------------------------------------------------------------------------------- /bench/native.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | 3 | http.createServer((req, res) => { 4 | if (req.url === '/favicon.ico') return; 5 | if (req.url === '/') return res.end('Hello'); 6 | }).listen(3000); 7 | -------------------------------------------------------------------------------- /bench/polka.js: -------------------------------------------------------------------------------- 1 | const polka = require('../packages/polka'); 2 | 3 | function one(req, res, next) { 4 | req.one = true; 5 | next(); 6 | } 7 | 8 | function two(req, res, next) { 9 | req.two = true; 10 | next(); 11 | } 12 | 13 | polka() 14 | .use(one, two) 15 | .get('/favicon.ico', _ => {}) 16 | .get('/', (req, res) => res.end('Hello')) 17 | .get('/user/:id', (req, res) => { 18 | res.end(`User: ${req.params.id}`); 19 | }) 20 | .listen(3000); 21 | -------------------------------------------------------------------------------- /bench/readme.md: -------------------------------------------------------------------------------- 1 | # Benchmarks 2 | 3 | All apps employ two global middlewares with `req` mutations, an empty `GET` route handler for `favicon.ico` and a `GET` handler for the `/users/:id`, returning a `User: {id}` string response. 4 | 5 | Results are taken after 1 warm-up run. The tool used for results is the following: 6 | 7 | ```sh 8 | $ wrk -t8 -c100 -d30s http://localhost:3000/user/123 9 | ``` 10 | 11 | > Please remember that _your application code_ is most likely the slowest part of your application!
Switching from Express to Polka will (likely) not guarantee the same performance gains. 12 | 13 | 14 | ## Node v9.1.0 15 | 16 | ``` 17 | #=> POLKA 18 | Thread Stats Avg Stdev Max +/- Stdev 19 | Latency 2.39ms 173.68us 7.90ms 89.40% 20 | Req/Sec 5.03k 110.03 5.80k 70.42% 21 | 1204800 requests in 30.10s, 124.09MB read 22 | Requests/sec: 40022.18 23 | Transfer/sec: 4.12MB 24 | 25 | #=> EXPRESS 26 | Thread Stats Avg Stdev Max +/- Stdev 27 | Latency 3.24ms 388.15us 9.84ms 92.78% 28 | Req/Sec 3.72k 105.01 4.38k 63.96% 29 | 889146 requests in 30.01s, 111.08MB read 30 | Requests/sec: 29623.83 31 | Transfer/sec: 3.70MB 32 | ``` 33 | 34 | ## Node v8.9.0 35 | 36 | ``` 37 | #=> POLKA 38 | Thread Stats Avg Stdev Max +/- Stdev 39 | Latency 2.59ms 186.29us 5.82ms 67.71% 40 | Req/Sec 4.66k 142.28 5.29k 68.67% 41 | 1115653 requests in 30.10s, 114.91MB read 42 | Requests/sec: 37059.21 43 | Transfer/sec: 3.82MB 44 | 45 | #=> EXPRESS 46 | Thread Stats Avg Stdev Max +/- Stdev 47 | Latency 3.41ms 347.21us 8.07ms 69.60% 48 | Req/Sec 3.53k 104.84 3.91k 69.83% 49 | 844077 requests in 30.01s, 105.45MB read 50 | Requests/sec: 28127.26 51 | Transfer/sec: 3.51MB 52 | ``` 53 | 54 | 55 | ## Node v6.11.1 56 | 57 | ``` 58 | #=> POLKA 59 | Thread Stats Avg Stdev Max +/- Stdev 60 | Latency 3.16ms 208.50us 6.93ms 75.13% 61 | Req/Sec 3.82k 101.28 5.34k 75.58% 62 | 911888 requests in 30.01s, 93.92MB read 63 | Requests/sec: 30384.16 64 | Transfer/sec: 3.13MB 65 | 66 | #=> EXPRESS 67 | Thread Stats Avg Stdev Max +/- Stdev 68 | Latency 4.61ms 289.07us 14.19ms 75.35% 69 | Req/Sec 2.61k 62.88 2.92k 70.71% 70 | 623917 requests in 30.01s, 77.95MB read 71 | Requests/sec: 20788.27 72 | Transfer/sec: 2.60MB 73 | ``` 74 | -------------------------------------------------------------------------------- /examples/async-json/index.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | const send = require('@polka/send-type'); 3 | const polka = require('polka'); 4 | 5 | const { PORT=3000 } = process.env; 6 | const API = 'https://hnpwa.com/api/v0'; 7 | 8 | function load(type) { 9 | return fetch(`${API}/${type}.json`).then(r => r.json()); 10 | } 11 | 12 | polka() 13 | .get('/:type?', async (req, res) => { 14 | let type = req.params.type || 'news'; 15 | let data = await load(type).catch(err => { 16 | send(res, 404); 17 | }); 18 | send(res, 200, data); 19 | }) 20 | .listen(PORT, () => { 21 | console.log(`> Running on localhost:${PORT}`); 22 | }); 23 | -------------------------------------------------------------------------------- /examples/async-json/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "start": "node index" 4 | }, 5 | "dependencies": { 6 | "@polka/send-type": "latest", 7 | "node-fetch": "^2.6.1", 8 | "polka": "latest" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/async-json/readme.md: -------------------------------------------------------------------------------- 1 | # Example: Async 2 | 3 | > **WARNING** This will only work with Node 7.4 or later! 4 | 5 | This example makes use of Node's built-in `async`/`await` — no compiling required! 6 | 7 | It forwards all requests to [HNPWA's JSON API](https://hnpwa.com/), utilizing [`node-fetch`](https://github.com/bitinn/node-fetch) for the server-side requests. 8 | 9 | ## Setup 10 | 11 | ```sh 12 | $ npm install 13 | $ npm start 14 | ``` 15 | 16 | ## Usage 17 | 18 | There are only a few valid paths: `/`, `/news`, `/newest`, `/jobs`, `/ask`, and `/show`. 19 | 20 | Anything else will return `(404) Not Found`. 21 | 22 | ```sh 23 | $ curl localhost:3000 24 | #=> (200) JSON 25 | $ curl localhost:3000/news 26 | #=> (200) JSON 27 | $ curl localhost:3000/foobar 28 | #=> (404) Not Found 29 | ``` 30 | -------------------------------------------------------------------------------- /examples/sub-app/index.js: -------------------------------------------------------------------------------- 1 | const polka = require('polka'); 2 | const users = require('./users'); 3 | 4 | const { PORT=3000 } = process.env; 5 | 6 | function reply(req, res) { 7 | res.end(`Main: Hello from ${req.method} ${req.url}`); 8 | } 9 | 10 | // Main app 11 | polka() 12 | .get('/', reply) 13 | .get('/about', reply) 14 | .use('users', users) 15 | .listen(PORT, () => { 16 | console.log(`> Running on localhost:${PORT}`); 17 | }); 18 | -------------------------------------------------------------------------------- /examples/sub-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "start": "node index" 4 | }, 5 | "dependencies": { 6 | "polka": "latest" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/sub-app/readme.md: -------------------------------------------------------------------------------- 1 | # Example: Sub-Application 2 | 3 | This example mounts a `/users` sub-application onto the "main" application. 4 | 5 | ## Setup 6 | 7 | ```sh 8 | $ npm install 9 | $ npm start 10 | ``` 11 | 12 | ## Usage 13 | 14 | ```sh 15 | $ curl localhost:3000 16 | #=> (200) Main: Hello from GET / 17 | 18 | $ curl localhost:3000/about 19 | #=> (200) Main: Hello from GET /about 20 | 21 | $ curl localhost:3000/users 22 | #=> (200) Sub: Howdy from GET /users 23 | 24 | $ curl localhost:3000/users/123 25 | #=> (200) Sub: Howdy from GET /users/123 26 | 27 | $ curl -X PUT localhost:3000/users/123 28 | #=> (201) Sub: Updated user via PUT /users/123 29 | ``` 30 | -------------------------------------------------------------------------------- /examples/sub-app/users.js: -------------------------------------------------------------------------------- 1 | const polka = require('polka'); 2 | 3 | module.exports = polka() 4 | .get('/:id?', (req, res) => { 5 | res.end(`Sub: Howdy from ${req.method} ${req.url}`); 6 | }) 7 | .put('/:id', (req, res) => { 8 | res.statusCode = 201; // why not? 9 | res.end(`Sub: Updated user via ${req.method} ${req.url}`); 10 | }); 11 | -------------------------------------------------------------------------------- /examples/with-afterjs/.gitignore: -------------------------------------------------------------------------------- 1 | .env.*.local 2 | build 3 | -------------------------------------------------------------------------------- /examples/with-afterjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "start": "razzle start", 4 | "build": "razzle build", 5 | "test": "razzle test --env=jsdom", 6 | "start:prod": "NODE_ENV=production node build/server.js" 7 | }, 8 | "dependencies": { 9 | "@jaredpalmer/after": "latest", 10 | "polka": "latest", 11 | "razzle": "^0.8.11", 12 | "react": "^16.2.0", 13 | "react-dom": "^16.2.0", 14 | "react-helmet": "^5.2.0", 15 | "react-router-dom": "^4.2.2", 16 | "serve-static": "^1.13.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/with-afterjs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukeed/polka/c36cf8b5d1a57164e64fdd7d4e0d0f80ab262dc0/examples/with-afterjs/public/favicon.ico -------------------------------------------------------------------------------- /examples/with-afterjs/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | -------------------------------------------------------------------------------- /examples/with-afterjs/readme.md: -------------------------------------------------------------------------------- 1 | # Example: Razzle + After.js 2 | 3 | > Ported from Razzle's [`examples/with-afterjs`](https://github.com/jaredpalmer/razzle/tree/master/examples/with-afterjs) — _nearly_ identical! 4 | 5 | This example combines Polka with [Razzle](https://github.com/jaredpalmer/razzle) and [After.js](https://github.com/jaredpalmer/after.js), creating a robust & performant server-rendered React application. 6 | 7 | Setup includes a production build, a production server, and live-reload [HMR](https://webpack.js.org/concepts/hot-module-replacement/) for _both_ client and server! :tada: 8 | 9 | ## Setup 10 | 11 | ```sh 12 | $ npm install 13 | # develop / HMR 14 | $ npm start 15 | # production 16 | $ npm run build 17 | $ npm run start:prod 18 | ``` 19 | 20 | ## Usage 21 | 22 | Open a browser to `localhost:3000`! 23 | 24 | To use the built-in live-reload / HMR development server, run: 25 | 26 | ```sh 27 | $ npm start 28 | ``` 29 | 30 | > The dev-server also runs on Polka! :dancers: 31 | -------------------------------------------------------------------------------- /examples/with-afterjs/src/About.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | export default class About extends Component { 4 | render() { 5 | return
about
; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/with-afterjs/src/Home.css: -------------------------------------------------------------------------------- 1 | .Home { 2 | text-align: center; 3 | } 4 | .Home-logo { 5 | animation: logo-spin infinite 20s linear; 6 | height: 80px; 7 | } 8 | 9 | .Home-header { 10 | background-color: #222; 11 | height: 150px; 12 | padding: 20px; 13 | color: white; 14 | } 15 | 16 | .Home-intro { 17 | font-size: large; 18 | } 19 | 20 | .Home-resources { 21 | list-style: none; 22 | } 23 | 24 | .Home-resources > li { 25 | display: inline-block; 26 | padding: 1rem; 27 | } 28 | 29 | @keyframes logo-spin { 30 | from { 31 | transform: rotate(0deg); 32 | } 33 | to { 34 | transform: rotate(360deg); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /examples/with-afterjs/src/Home.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import logo from './react.svg'; 3 | import './Home.css'; 4 | import { Link } from 'react-router-dom'; 5 | 6 | export default class Home extends Component { 7 | static async getInitialProps({ req, res, match, history, location, ...ctx }) { 8 | return { whatever: 'stuff' }; 9 | } 10 | 11 | render() { 12 | return ( 13 |
14 |
15 | logo 16 |

Welcome to After.js

17 |
18 |

19 | To get started, edit src/Home.js or{' '} 20 | src/About.jsand save to reload. 21 |

22 | About -> 23 |
24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/with-afterjs/src/client.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, 5 | Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 6 | } 7 | -------------------------------------------------------------------------------- /examples/with-afterjs/src/client.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { hydrate } from 'react-dom'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | import { ensureReady, After } from '@jaredpalmer/after'; 5 | import './client.css'; 6 | import routes from './routes'; 7 | 8 | ensureReady(routes).then(data => 9 | hydrate( 10 | 11 | 12 | , 13 | document.getElementById('root') 14 | ) 15 | ); 16 | 17 | if (module.hot) { 18 | module.hot.accept(); 19 | } 20 | -------------------------------------------------------------------------------- /examples/with-afterjs/src/index.js: -------------------------------------------------------------------------------- 1 | import app from './server'; 2 | 3 | let { handler, server } = app; 4 | 5 | server.listen(process.env.PORT || 3000, () => { 6 | console.log('🚀 started'); 7 | }); 8 | 9 | if (module.hot) { 10 | console.log('✅ Server-side HMR Enabled!'); 11 | 12 | module.hot.accept('./server', () => { 13 | console.log('🔁 HMR Reloading `./server`...'); 14 | server.removeListener('request', handler); 15 | let nxt = require('./server').default; 16 | server.on('request', nxt.handler); 17 | handler = nxt.handler; 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /examples/with-afterjs/src/react.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /examples/with-afterjs/src/routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { asyncComponent } from '@jaredpalmer/after'; 4 | 5 | export default [ 6 | { 7 | path: '/', 8 | exact: true, 9 | component: asyncComponent({ 10 | loader: () => import('./Home'), // required 11 | Placeholder: () =>
...LOADING...
, // this is optional, just returns null by default 12 | }), 13 | }, 14 | { 15 | path: '/about', 16 | exact: true, 17 | component: asyncComponent({ 18 | loader: () => import('./About'), // required 19 | Placeholder: () =>
...LOADING...
, // this is optional, just returns null by default 20 | }), 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /examples/with-afterjs/src/server.js: -------------------------------------------------------------------------------- 1 | import polka from 'polka'; 2 | import { render } from '@jaredpalmer/after'; 3 | import serve from 'serve-static'; 4 | import routes from './routes'; 5 | 6 | const assets = require(process.env.RAZZLE_ASSETS_MANIFEST); 7 | const statics = serve(process.env.RAZZLE_PUBLIC_DIR); 8 | 9 | const server = polka(); 10 | 11 | server 12 | .use(statics) 13 | .get('/*', async (req, res) => { 14 | try { 15 | const html = await render({ 16 | req, 17 | res, 18 | routes, 19 | assets, 20 | // Anything else you add here will be made available 21 | // within getInitialProps(ctx) 22 | // e.g a redux store... 23 | customThing: 'thing', 24 | }); 25 | res.end(html); 26 | } catch (error) { 27 | let json = JSON.stringify(error); 28 | res.setHeader('Content-Type', 'application/json'); 29 | res.setHeader('Content-Length', json.length); 30 | res.end(json); 31 | } 32 | }); 33 | 34 | export default server; 35 | -------------------------------------------------------------------------------- /examples/with-apollo/index.js: -------------------------------------------------------------------------------- 1 | const polka = require('polka'); 2 | const { json } = require('body-parser'); 3 | const { makeExecutableSchema } = require('graphql-tools'); 4 | const { graphqlExpress, graphiqlExpress } = require('apollo-server-express'); 5 | 6 | const { PORT=3000 } = process.env; 7 | 8 | const tasks = [ 9 | { id: 1, name: 'Go to Market', complete: false }, 10 | { id: 2, name: 'Walk the dog', complete: true }, 11 | { id: 3, name: 'Take a nap', complete: false } 12 | ]; 13 | 14 | const typeDefs = ` 15 | type Task { 16 | id: Int! 17 | name: String! 18 | complete: Boolean! 19 | } 20 | 21 | type Query { 22 | tasks: [Task] 23 | task(id: Int!): Task 24 | } 25 | `; 26 | 27 | const resolvers = { 28 | Query: { 29 | tasks: () => tasks, 30 | task: (_, args) => tasks.find(o => o.id === args.id) 31 | } 32 | }; 33 | 34 | const schema = module.exports = makeExecutableSchema({ typeDefs, resolvers }); 35 | 36 | polka() 37 | .use(json()) 38 | .post('/graphql', graphqlExpress(req => ({ 39 | schema 40 | }))) 41 | .get('/graphiql', graphiqlExpress({ 42 | endpointURL: '/graphql' 43 | })) 44 | .listen(PORT, () => { 45 | console.log(`> Ready on localhost:${PORT}`) 46 | }); 47 | -------------------------------------------------------------------------------- /examples/with-apollo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "start": "node index" 4 | }, 5 | "dependencies": { 6 | "apollo-server-express": "^1.3.6", 7 | "body-parser": "^1.18.3", 8 | "graphql": "^0.13.2", 9 | "graphql-tools": "^3.0.2", 10 | "polka": "latest" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/with-apollo/readme.md: -------------------------------------------------------------------------------- 1 | # Example: Apollo Graphql 2 | 3 | Tiny example with [Apollo Graphql](https://www.apollographql.com/) 4 | 5 | It uses the [`Apollo Server`](https://github.com/apollographql/apollo-server) a community-maintained open-source GraphQL server. 6 | 7 | ## Setup 8 | ```sh 9 | $ npm install 10 | $ npm start 11 | ``` 12 | 13 | ## Usage 14 | You can use it with any apollo client or with the [Graphiql](https://github.com/graphql/graphiql) in [localhost](http://localhost:3000/graphiql) 15 | 16 | ## Available queries 17 | ``` 18 | { 19 | tasks { 20 | id 21 | name 22 | complete 23 | } 24 | } 25 | ``` 26 | 27 | ``` 28 | { 29 | task (id: Int!) { 30 | id 31 | name 32 | complete 33 | } 34 | } 35 | ``` 36 | 37 | ![Screenshot](screenshot.png) 38 | -------------------------------------------------------------------------------- /examples/with-apollo/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukeed/polka/c36cf8b5d1a57164e64fdd7d4e0d0f80ab262dc0/examples/with-apollo/screenshot.png -------------------------------------------------------------------------------- /examples/with-body-parser/index.js: -------------------------------------------------------------------------------- 1 | const polka = require('polka'); 2 | const { json } = require('body-parser'); 3 | const { PORT=3000 } = process.env; 4 | 5 | polka() 6 | .use(json()) 7 | .post('/', (req, res) => { 8 | res.writeHead(200, { 'Content-Type': 'application/json' }); 9 | let json = JSON.stringify(req.body); 10 | res.end(json); 11 | }) 12 | .listen(PORT, () => { 13 | console.log(`> Running on localhost:${PORT}`); 14 | }); 15 | -------------------------------------------------------------------------------- /examples/with-body-parser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "start": "node index" 4 | }, 5 | "dependencies": { 6 | "body-parser": "^1.18.2", 7 | "polka": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/with-body-parser/readme.md: -------------------------------------------------------------------------------- 1 | # Example: Body Parser 2 | 3 | Tiny example that echoes the JSON that it received. 4 | 5 | It uses the popular [`body-parser`](https://github.com/expressjs/body-parser) middleware from Express to _parse_ the incoming body. As per the middleware behavior, an object is made available at the `req.body` location. 6 | 7 | ## Setup 8 | 9 | ```sh 10 | $ npm install 11 | $ npm start 12 | ``` 13 | 14 | ## Usage 15 | 16 | There is only one route (`POST /`) for the purpose of this demo. 17 | 18 | ```sh 19 | $ curl localhost:3000 20 | #=> (501) Not Implemented 21 | 22 | $ curl localhost:3000 -X POST -d '{"hello":"world"}' 23 | #=> (200) {} 24 | 25 | $ curl localhost:3000 -X POST -d '{"hello":"world"}' -H "content-type: application/json" 26 | #=> (200) {"hello":"world"} 27 | ``` 28 | -------------------------------------------------------------------------------- /examples/with-firebase-admin/.gitignore: -------------------------------------------------------------------------------- 1 | firebase.json 2 | secret.json 3 | .cache 4 | dist 5 | public 6 | -------------------------------------------------------------------------------- /examples/with-firebase-admin/api/index.js: -------------------------------------------------------------------------------- 1 | const polka = require('polka'); 2 | const items = require('./items'); 3 | 4 | module.exports = polka().use('items', items); 5 | -------------------------------------------------------------------------------- /examples/with-firebase-admin/api/items.js: -------------------------------------------------------------------------------- 1 | const polka = require('polka'); 2 | const send = require('@polka/send-type'); 3 | const { DB, isUser } = require('./services'); 4 | 5 | const ITEMS = DB.ref('items'); 6 | 7 | module.exports = polka() 8 | .use(isUser) // verify token 9 | .get('/', (req, res) => { 10 | ITEMS.once('value').then(s => { 11 | let k, out=[], obj=s.val(); 12 | for (k in obj) { 13 | (obj[k].id=k) && out.push(obj[k]); 14 | } 15 | send(res, 200, out); 16 | }); 17 | }) 18 | .post('/', (req, res) => { 19 | let obj = req.body; 20 | ITEMS.push(req.body).then(data => { 21 | obj.id = data.key; 22 | send(res, 201, obj); 23 | }); 24 | }) 25 | .get('/:id', (req, res) => { 26 | ITEMS.child(req.params.id).then(s => { 27 | let obj = s.val(); 28 | obj.id = s.key; 29 | send(res, 200, obj); 30 | }); 31 | }) 32 | .put('/:id', (req, res) => { 33 | let obj = req.body; 34 | ITEMS.child(req.params.id).set(obj).then(data => { 35 | send(res, 200, obj); 36 | }); 37 | }) 38 | .delete('/:id', (req, res) => { 39 | ITEMS.child(req.params.id).remove().then(_ => { 40 | send(res, 204); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /examples/with-firebase-admin/api/services.js: -------------------------------------------------------------------------------- 1 | const send = require('@polka/send-type'); 2 | const firebase = require('firebase-admin'); 3 | const secret = require('../secret.json'); 4 | 5 | firebase.initializeApp({ 6 | credential: firebase.credential.cert(secret), 7 | databaseURL: 'https://temp123-1414d.firebaseio.com' 8 | }); 9 | 10 | const Auth = exports.Auth = firebase.auth(); 11 | const DB = exports.DB = firebase.database(); 12 | 13 | // Middleware: Authenticate Incoming Request 14 | exports.isUser = (req, res, next) => { 15 | let token = req.headers['authorization']; 16 | if (!token) return send(res, 401, 'Token not found.'); 17 | token = token.split(' ')[1]; // strip "Bearer" 18 | Auth.verifyIdToken(token) 19 | .then(user => (req.user=user) && next()) 20 | .catch(err => send(res, 401, 'Invalid token.')); 21 | }; 22 | -------------------------------------------------------------------------------- /examples/with-firebase-admin/client/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "comments": false, 3 | "presets": [ 4 | ["env", { 5 | "loose": true, 6 | "uglify": true, 7 | "modules": false, 8 | "exclude": [ 9 | "transform-regenerator", 10 | "transform-es2015-typeof-symbol" 11 | ] 12 | }] 13 | ], 14 | "plugins": [ 15 | "transform-class-properties", 16 | "transform-react-constant-elements", 17 | ["transform-react-jsx", { "pragma": "h" }], 18 | ["jsx-pragmatic", { 19 | "module": "preact", 20 | "export": "h", 21 | "import": "h" 22 | }] 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /examples/with-firebase-admin/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Polka • Firebase • Preact • TodoMVC 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/with-firebase-admin/client/index.js: -------------------------------------------------------------------------------- 1 | import { h, render } from 'preact'; 2 | import App from './tags/App'; 3 | import 'todomvc-app-css'; 4 | 5 | let root = document.getElementById('root'); 6 | let diff = root.firstElementChild; 7 | diff = render(, root, diff); 8 | 9 | if (module.hot) { 10 | module.hot.accept(); 11 | } 12 | -------------------------------------------------------------------------------- /examples/with-firebase-admin/client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "parcel build -d ../public index.html", 5 | "watch": "parcel watch -d ../public index.html" 6 | }, 7 | "dependencies": { 8 | "is-ready": "^1.0.0", 9 | "obj-str": "^1.0.0", 10 | "preact": "^8.2.7", 11 | "preact-router": "^2.6.0", 12 | "todomvc-app-css": "^2.1.0", 13 | "todomvc-common": "^1.0.4" 14 | }, 15 | "devDependencies": { 16 | "babel-plugin-jsx-pragmatic": "^1.0.2", 17 | "babel-plugin-transform-class-properties": "^6.24.1", 18 | "babel-plugin-transform-react-constant-elements": "^6.23.0", 19 | "babel-plugin-transform-react-jsx": "^6.24.1", 20 | "babel-preset-env": "^1.6.1", 21 | "parcel-bundler": "^1.5.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/with-firebase-admin/client/tags/App.js: -------------------------------------------------------------------------------- 1 | import isReady from 'is-ready'; 2 | import { h, Component } from 'preact'; 3 | import { Router, route } from 'preact-router'; 4 | import { isUser } from '../utils/local'; 5 | import CONFIG from '../firebase.json'; 6 | import TodoMVC from './TodoMVC'; 7 | import Login from './Login'; 8 | 9 | export default class App extends Component { 10 | state = { user:isUser() } 11 | 12 | onRoute = obj => { 13 | let isUser = !!this.state.user; 14 | let isLogin = !!~obj.url.indexOf('login'); 15 | if (!isUser) return route('/login', true); 16 | if (isUser && isLogin) return route('/', true); 17 | } 18 | 19 | componentWillMount() { 20 | isReady('firebase').then(_ => { 21 | let user = this.state.user; 22 | let app = firebase.initializeApp(CONFIG) 23 | isReady('firebase.auth').then(_ => { 24 | app.auth().onAuthStateChanged(obj => { 25 | let isNew = (!!user !== !!obj); 26 | this.setState({ user:obj }, _ => { 27 | isNew && route(obj ? '/' : '/login', true); 28 | }); 29 | }); 30 | }); 31 | }); 32 | } 33 | 34 | render(_, state) { 35 | return ( 36 | 37 | 38 | 39 | 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/with-firebase-admin/client/tags/Footer.js: -------------------------------------------------------------------------------- 1 | import { Link } from 'preact-router/match'; 2 | 3 | export default function (props) { 4 | let num = props.active; 5 | let now = props.filter; 6 | 7 | return ( 8 |
9 | 10 | {num} { num > 1 ? 'items' : 'item' } left 11 | 12 | 13 |
    14 |
  • All
  • 15 |
  • Active
  • 16 |
  • Completed
  • 17 |
18 | 19 | { 20 | props.completed > 0 && ( 21 | 22 | ) 23 | } 24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /examples/with-firebase-admin/client/tags/Item.js: -------------------------------------------------------------------------------- 1 | import cx from 'obj-str'; 2 | import { h, Component } from 'preact'; 3 | 4 | export default class TodoItem extends Component { 5 | state = { text:'' } 6 | 7 | onSubmit = e => { 8 | if (!this.props.editing) return; 9 | let obj = this.props.data; 10 | let val = e.target.value.trim(); 11 | val ? this.props.onSave(obj, val): this.props.onDestroy(obj); 12 | } 13 | 14 | handleEdit = () => { 15 | let obj = this.props.data; 16 | this.setState({ text:obj.title }); 17 | this.props.onEdit(obj.id); 18 | } 19 | 20 | onToggle = e => { 21 | e.preventDefault(); 22 | let obj = this.props.data; 23 | this.props.onToggle(obj); 24 | } 25 | 26 | onKeydown = e => { 27 | if (e.which === 13) { 28 | this.onSubmit(e); 29 | } else if (e.which === 27) { 30 | this.setState({ text:'' }); 31 | this.props.onCancel(); 32 | } 33 | } 34 | 35 | onDelete = () => { 36 | this.props.onDestroy(this.props.data); 37 | } 38 | 39 | componentDidUpdate() { 40 | let node = this.base && this.base.querySelector('.edit'); 41 | if (node) node.focus(); 42 | } 43 | 44 | render(props, state) { 45 | let editing = props.editing; 46 | let { title, completed } = props.data; 47 | 48 | return ( 49 |
  • 50 |
    51 | 52 | 53 |
    55 | { editing && ( 56 | 58 | ) } 59 |
  • 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /examples/with-firebase-admin/client/tags/Login.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'preact'; 2 | 3 | export default class Login extends Component { 4 | state = { email:'', password:'' } 5 | 6 | onSubmit = ev => { 7 | ev.preventDefault(); 8 | let { email, password } = this.state; 9 | if (!email || !password) return console.error('[TODO]: An email and password are required!'); 10 | firebase.auth().signInWithEmailAndPassword(email, password).catch(err => { 11 | console.error('[AUTH]', err.message); 12 | }).then(data => { 13 | console.log('> data', data); 14 | }) 15 | } 16 | 17 | onInput = key => ev => { 18 | this.setState({ [key]: ev.target.value }); 19 | } 20 | 21 | render() { 22 | return ( 23 |
    24 |

    Login

    25 | 26 | 27 | 28 |
    29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/with-firebase-admin/client/tags/TodoMVC.js: -------------------------------------------------------------------------------- 1 | import isReady from 'is-ready'; 2 | import { Component } from 'preact'; 3 | import { get, post, put, del } from '../utils/api'; 4 | import TodoFooter from './Footer'; 5 | import TodoItem from './Item'; 6 | 7 | const toAuth = str => (str && { Authorization:`Bearer: ${str}` }); 8 | 9 | const FILTERS = { 10 | all: obj => true, 11 | active: obj => !obj.completed, 12 | completed: obj => obj.completed 13 | }; 14 | 15 | export default class Todos extends Component { 16 | state = { editing:'', items:[], filter:'all' } 17 | 18 | onAddItem = e => { 19 | if (e.keyCode !== 13) return; 20 | let title = e.target.value.trim(); 21 | if (!title) return; // exit 22 | let items = this.state.items; 23 | post('items', { completed:false, title }).then(obj => { 24 | this.setState({ items:items.concat(obj) }); 25 | e.target.value = null; // reset 26 | }); 27 | } 28 | 29 | onEdit = id => this.setState({ editing:id }) 30 | onCancel = _ => this.setState({ editing:null }) 31 | 32 | onSave = (obj, text) => { 33 | obj.title = text; 34 | put(`items/${obj.id}`, obj).then(_ => { 35 | let items = this.state.items.map(x => x.id === obj.id ? obj : x); 36 | this.setState({ editing:null, items }); 37 | }); 38 | } 39 | 40 | onToggle = obj => { 41 | obj.completed = !obj.completed; 42 | put(`items/${obj.id}`, obj).then(_ => { 43 | let items = this.state.items.map(x => x.id === obj.id ? obj : x); 44 | this.setState({ items }); 45 | }); 46 | } 47 | 48 | clearCompleted = e => { 49 | let items = this.state.items; 50 | return Promise.all( 51 | items.filter(FILTERS.completed).map(o => del(`items/${o.id}`)) 52 | ).then(_ => { 53 | this.setState({ 54 | items: items.filter(FILTERS.active) 55 | }); 56 | }); 57 | } 58 | 59 | onDestroy = obj => { 60 | let id = obj.id; 61 | del(`items/${id}`).then(_ => { 62 | let items = this.state.items.filter(x => x.id !== id); 63 | this.setState({ items }); 64 | }); 65 | } 66 | 67 | componentDidMount() { 68 | let filter = this.props.filter || 'all'; 69 | get('items').then(arr => { 70 | this.setState({ items:arr, filter }); 71 | }); 72 | } 73 | 74 | componentWillReceiveProps(nxt) { 75 | this.setState({ filter:nxt.filter || 'all' }); 76 | } 77 | 78 | render(_, state) { 79 | let items = state.items; 80 | let visible = items.filter(FILTERS[state.filter]); 81 | let numActive = items.reduce((x, obj) => x + Number(!obj.completed), 0); 82 | let numComplete = items.length - numActive; 83 | 84 | return ( 85 |
    86 |
    87 |

    todos

    88 | 91 |
    92 | 93 | { 94 | items.length > 0 && ( 95 |
    96 | 97 |
      98 | { 99 | visible.map(obj => ( 100 | 106 | )) 107 | } 108 |
    109 |
    110 | ) 111 | } 112 | 113 | { 114 | (numActive || numComplete) > 0 && ( 115 | 117 | ) 118 | } 119 |
    120 | ); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /examples/with-firebase-admin/client/utils/api.js: -------------------------------------------------------------------------------- 1 | import { getToken } from './local'; 2 | 3 | const API = '/api/'; 4 | 5 | const HEADERS = { 6 | 'Content-Type': 'application/json;charset=UTF-8', 7 | 'Accept': 'application/json, text/plain, */*' 8 | }; 9 | 10 | function handle(r) { 11 | let act = r.ok ? 'resolve' : 'reject'; 12 | let type = r.status === 204 ? 'text' : 'json'; 13 | return r[type]().then(data => Promise[act](data)); 14 | } 15 | 16 | function send(method, uri, data, opts) { 17 | opts = opts || {}; 18 | opts.method = method; 19 | opts.headers = HEADERS; 20 | let token = getToken(); // fresh check on localstorage 21 | token && (opts.headers.Authorization = `Bearer ${token}`); 22 | data && (opts.body = JSON.stringify(data)); 23 | return fetch(`${API}${uri}`, opts).then(handle); 24 | } 25 | 26 | export const get = send.bind(null, 'get'); 27 | export const put = send.bind(null, 'put'); 28 | export const post = send.bind(null, 'post'); 29 | export const del = send.bind(null, 'delete'); 30 | -------------------------------------------------------------------------------- /examples/with-firebase-admin/client/utils/local.js: -------------------------------------------------------------------------------- 1 | import CONFIG from '../firebase.json'; 2 | 3 | const STORAGE = localStorage; 4 | const KEYID = `firebase:authUser:${CONFIG.apiKey}:[DEFAULT]`; 5 | 6 | export function isUser() { 7 | let str = STORAGE.getItem(KEYID); 8 | return str && JSON.parse(str); 9 | } 10 | 11 | export function getToken() { 12 | let user = isUser() || {}; 13 | return (user.stsTokenManager || {}).accessToken; 14 | } 15 | -------------------------------------------------------------------------------- /examples/with-firebase-admin/index.js: -------------------------------------------------------------------------------- 1 | const polka = require('polka'); 2 | const { json } = require('body-parser'); 3 | const cors = require('cors')({ origin:true }); 4 | const serve = require('serve-static')('public'); 5 | const compress = require('compression')(); 6 | 7 | const { PORT=3000 } = process.env; 8 | 9 | polka() 10 | .use(cors, compress, json(), serve) 11 | .use('api', require('./api')) 12 | .listen(PORT, () => { 13 | console.log(`> Running on localhost:${PORT}`); 14 | }); 15 | -------------------------------------------------------------------------------- /examples/with-firebase-admin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "start": "node index" 4 | }, 5 | "dependencies": { 6 | "@polka/send-type": "latest", 7 | "body-parser": "^1.18.2", 8 | "compression": "^1.7.1", 9 | "cors": "^2.8.4", 10 | "firebase-admin": "^5.8.1", 11 | "polka": "latest", 12 | "serve-static": "^1.13.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/with-firebase-admin/readme.md: -------------------------------------------------------------------------------- 1 | # Example: Firebase-Admin TodoMVC 2 | 3 | This example uses Polka as an API layer (via [`firebase-admin`](https://www.npmjs.com/package/firebase-admin)) and as a static asset server (via [`serve-static`](https://www.npmjs.com/package/serve-static)). 4 | 5 | It's also using [Parcel](https://github.com/parcel-bundler/parcel), a zero-config bundler, to compile a [Preact](https://github.com/developit/preact) implementation of the [TodoMVC](http://todomvc.com) application. 6 | 7 | Once up and running, you will be forced to Register/Login (`/login`) before accessing the TodoMVC app. All TodoMVC actions are sent to the Firebase Real-time Database for confirmation _before_ applying the change(s) in the browser. 8 | 9 | > This is partly to illustrate how quickly the Firebase RTDB operates! 10 | 11 | ## Setup 12 | 13 | 1. You must create a [new Firebase project](https://firebase.google.com/docs/web/setup) 14 | 15 | _For the purposes of this demo, it's free!_ 16 | 17 | 2. You must retrieve a [Firebase Service Account](https://firebase.google.com/docs/admin/setup#add_firebase_to_your_app). 18 | 19 | _**Hint:** Click "Generate New Private Key"_ 20 | 21 | 3. Save the Account credentials to `with-firebase/secret.json` 22 | 23 | _This file is accessed inside `with-firebase/api/services.js` to connect to your Database._ 24 | 25 | 4. Save your [Firebase WEB config](https://firebase.google.com/docs/database/web/start) to `with-firebase/client/firebase.json`. 26 | 27 | _This is used to connect the Firebase client to your Database from within the browser._ 28 | 29 | 5. Install & build dependencies 30 | 31 | ```sh 32 | # Install server dependencies 33 | $ npm install 34 | # Install client dependencies & build 35 | $ cd client && npm install && npm run build 36 | # Run the server! 37 | $ cd .. && npm start 38 | ``` 39 | 40 | ## Usage 41 | 42 | Open a browser to `localhost:3000`! 43 | 44 | You can also try interacting with the API, but all routes require a Firebase Token identifier. 45 | > AKA, it won't work :D 46 | 47 | ```sh 48 | $ curl localhost:3000/api/items 49 | #=> (401) Token not found. 50 | 51 | $ curl -H "Authorization: Bearer foobar" localhost:3000/api/items 52 | #=> (401) Invalid token. 53 | ``` 54 | -------------------------------------------------------------------------------- /examples/with-graphql/index.js: -------------------------------------------------------------------------------- 1 | const polka = require('polka'); 2 | const { json } = require('body-parser'); 3 | const send = require('@polka/send-type'); 4 | const { graphql, buildSchema } = require('graphql'); 5 | 6 | const { PORT=3000 } = process.env; 7 | 8 | const tasks = [ 9 | { id:1, name:'Go to Market', complete:false }, 10 | { id:2, name:'Walk the dog', complete:true }, 11 | { id:3, name:'Take a nap', complete:false } 12 | ]; 13 | 14 | const schema = buildSchema(` 15 | type Task { 16 | id: Int! 17 | name: String! 18 | complete: Boolean! 19 | } 20 | 21 | type Query { 22 | tasks: [Task] 23 | task(id: Int!): Task 24 | } 25 | `); 26 | 27 | let ctx = { 28 | tasks: () => tasks, 29 | task: (args) => tasks.find(o => o.id === args.id) 30 | }; 31 | 32 | polka() 33 | .use(json()) 34 | .post('/', (req, res) => { 35 | let { query } = req.body; 36 | // We could use `async` & `await` here 37 | // but requires Node 8.x environment to run 38 | graphql(schema, query, ctx).then(data => { 39 | send(res, 200, data); 40 | }); 41 | }) 42 | .listen(PORT, () => { 43 | console.log(`> Ready on localhost:${PORT}`); 44 | }); 45 | -------------------------------------------------------------------------------- /examples/with-graphql/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "start": "node index" 4 | }, 5 | "dependencies": { 6 | "@polka/send-type": "^0.3.4", 7 | "body-parser": "^1.18.2", 8 | "graphql": "^0.13.1", 9 | "polka": "^0.3.4" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/with-graphql/readme.md: -------------------------------------------------------------------------------- 1 | # Example: GraphQL 2 | 3 | Tiny example that exposes a [GraphQL](https://github.com/graphql/graphql-js) API via JSON queries. 4 | 5 | It uses the popular [`body-parser`](https://github.com/expressjs/body-parser) middleware from Express to _parse_ the incoming body. As per the middleware behavior, an object is made available at the `req.body` location. 6 | 7 | An inline `tasks` dataset is included for this example -- you wouldn't do this in a real project! :laughing: 8 | 9 | > _Disclaimer:_ I don't actually use GraphQL, so am not sure if this is the best way to handle it in a Node.js server.
    It seems to work just fine without a load of dependencies; however, there are many ways & tools available! 10 | 11 | ## Setup 12 | 13 | ```sh 14 | $ npm install 15 | $ npm start 16 | ``` 17 | 18 | ## Usage 19 | 20 | There is only one route (`POST /`) for the purpose of this demo. 21 | 22 | ```sh 23 | $ curl localhost:3000 24 | #=> (404) Not Found 25 | 26 | $ curl localhost:3000 -d '{"query":"{ tasks { id, name, complete } }"}' -H "content-type: application/json" 27 | #=> (200) {"data": {"tasks": [...] }} 28 | 29 | $ curl localhost:3000 -d '{"query":"{ task(id:2) { name, complete } }"}' -H "content-type: application/json" 30 | #=> (200) {"data":{"task":{"name":"Walk the dog","complete":true}}} 31 | ``` 32 | -------------------------------------------------------------------------------- /examples/with-https/index.js: -------------------------------------------------------------------------------- 1 | const { createServer } = require('https'); 2 | const { readFileSync } = require('fs'); 3 | const polka = require('polka'); 4 | 5 | const { PORT=3000 } = process.env; 6 | 7 | const options = { 8 | key: readFileSync('ssl/foobar.key'), 9 | cert: readFileSync('ssl/foobar.crt') 10 | }; 11 | 12 | // Main app 13 | const { handler } = polka().get('*', (req, res) => { 14 | res.end(`POLKA: Hello from ${req.pathname}`); 15 | }); 16 | 17 | // Mount Polka to HTTPS server 18 | createServer(options, handler).listen(PORT, _ => { 19 | console.log(`> Running on https://localhost:${PORT}`); 20 | }); 21 | -------------------------------------------------------------------------------- /examples/with-https/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "start": "node index" 4 | }, 5 | "dependencies": { 6 | "polka": "^0.2.3" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/with-https/readme.md: -------------------------------------------------------------------------------- 1 | # Example: HTTPS 2 | 3 | This example shows how to mount Polka inside a HTTPS server. 4 | 5 | All requests are handled by Polka, echoing the `req.pathname` it received. 6 | 7 | ## Setup 8 | 9 | ```sh 10 | $ npm install 11 | # Import or Generate SSL keys 12 | $ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ssl/foobar.key -out ssl/foobar.crt 13 | $ npm start 14 | ``` 15 | 16 | > **Note:** You'll likely see a "Not Secure" warning — it's because of the fake certificate we just generated. 17 | 18 | ## Usage 19 | 20 | ```sh 21 | $ curl -k https://localhost:3000 22 | #=> (200) POLKA: Hello from / 23 | 24 | $ curl -k https://localhost:3000/users/123 25 | #=> (200) POLKA: Hello from /users/123 26 | ``` 27 | -------------------------------------------------------------------------------- /examples/with-https/ssl/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukeed/polka/c36cf8b5d1a57164e64fdd7d4e0d0f80ab262dc0/examples/with-https/ssl/.gitkeep -------------------------------------------------------------------------------- /examples/with-morgan/index.js: -------------------------------------------------------------------------------- 1 | const polka = require('polka'); 2 | const morgan = require('morgan'); 3 | const { send } = require('./util'); 4 | const items = require('./items'); 5 | 6 | const { PORT=3000 } = process.env; 7 | 8 | // init Polka (HTTP) server 9 | polka() 10 | .use(morgan('dev')) 11 | .use('/items', items) 12 | .get('/', (req, res) => { 13 | send(res, 'Index'); 14 | }) 15 | .listen(PORT, () => { 16 | console.log(`> Ready on localhost:${PORT}`); 17 | }); 18 | -------------------------------------------------------------------------------- /examples/with-morgan/items.js: -------------------------------------------------------------------------------- 1 | const polka = require('polka'); 2 | const { send } = require('./util'); 3 | 4 | module.exports = polka() 5 | .get('/', (req, res) => { 6 | send(res, 'items@index'); 7 | }) 8 | .post('/', (req, res) => { 9 | send(res, 'items@create'); 10 | }) 11 | .get('/:id', (req, res) => { 12 | send(res, `items@show(${req.params.id})`); 13 | }) 14 | .put('/:id', (req, res) => { 15 | send(res, `items@edit(${req.params.id})`); 16 | }) 17 | .delete('/:id', (req, res) => { 18 | send(res, `items@delete(${req.params.id})`); 19 | }); 20 | -------------------------------------------------------------------------------- /examples/with-morgan/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "start": "node index" 4 | }, 5 | "dependencies": { 6 | "morgan": "latest", 7 | "polka": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/with-morgan/readme.md: -------------------------------------------------------------------------------- 1 | # Example: Morgan logger 2 | 3 | This example shows how to mount [`morgan`](https://www.npmjs.com/package/morgan) as a global logger within a Polka application. 4 | 5 | Additionally, a sub-application is attached to the `/items` base path, illustrating that all routes & methods are recorded correctly! 6 | 7 | ## Setup 8 | 9 | ```sh 10 | $ npm install 11 | $ npm start 12 | ``` 13 | 14 | ## Usage 15 | 16 | Open a browser to `localhost:3000` or run the `curl` commands below. 17 | 18 | > Check your Terminal for `morgan` output! :tada: 19 | 20 | ```sh 21 | $ curl localhost:3000 22 | #=> (200) Index 23 | 24 | $ curl localhost:3000/items 25 | #=> (200) items@index 26 | 27 | $ curl localhost:3000/items/123 28 | #=> (200) items@show(123) 29 | 30 | $ curl localhost:3000/items -X POST 31 | #=> (200) items@create 32 | 33 | $ curl localhost:3000/items/123 -X PUT 34 | #=> (200) items@update(123) 35 | 36 | $ curl localhost:3000/items/123 -X DELETE 37 | #=> (200) items@delete(123) 38 | 39 | $ curl localhost:3000/foobar 40 | #=> (404) Not Found 41 | ``` 42 | -------------------------------------------------------------------------------- /examples/with-morgan/util.js: -------------------------------------------------------------------------------- 1 | // SUPER basic send() helper 2 | // ~> Just setting Content-Length 3 | // ~> because Morgan wants to print it 4 | exports.send = function (res, data) { 5 | res.setHeader('Content-Length', data.length); 6 | res.end(data); 7 | } 8 | -------------------------------------------------------------------------------- /examples/with-nextjs/.gitignore: -------------------------------------------------------------------------------- 1 | .next 2 | node_modules 3 | package-lock.json 4 | -------------------------------------------------------------------------------- /examples/with-nextjs/index.js: -------------------------------------------------------------------------------- 1 | const next = require('next'); 2 | const polka = require('polka'); 3 | 4 | const { PORT=3000, NODE_ENV } = process.env; 5 | 6 | const dev = NODE_ENV !== 'production'; 7 | const app = next({ dev }); 8 | const handle = app.getRequestHandler(); 9 | 10 | app.prepare().then(() => { 11 | polka() 12 | .get('*', handle) 13 | .listen(PORT, () => { 14 | console.log(`> Ready on http://localhost:${PORT}`); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /examples/with-nextjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "node index.js", 4 | "build": "next build", 5 | "start": "NODE_ENV=production node index.js" 6 | }, 7 | "dependencies": { 8 | "next": "8.1.0", 9 | "polka": "latest", 10 | "react": "16.8.6", 11 | "react-dom": "16.8.6" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/with-nextjs/pages/about.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'next/link'; 3 | 4 | export default () => ( 5 |
    6 |

    About Page

    7 | 8 | Link to Home 9 | 10 |
    11 | ) 12 | -------------------------------------------------------------------------------- /examples/with-nextjs/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'next/link'; 3 | 4 | export default () => ( 5 |
    6 |

    Home Page

    7 | 8 | Link to About Page 9 | 10 |
    11 | ) 12 | -------------------------------------------------------------------------------- /examples/with-nextjs/readme.md: -------------------------------------------------------------------------------- 1 | # Example: Next.js 2 | 3 | It uses [`Next.js`](https://github.com/zeit/next.js) a Framework for server-rendered or statically-exported React apps. 4 | 5 | ## Setup 6 | ```sh 7 | $ npm install 8 | $ npm run build 9 | $ npm start 10 | ``` 11 | or 12 | ```sh 13 | $ npm install 14 | $ npm run dev 15 | ``` 16 | 17 | ## Usage 18 | Go to `localhost:3000` after starting the server and get the welcome message 19 | -------------------------------------------------------------------------------- /examples/with-nuxtjs/.gitignore: -------------------------------------------------------------------------------- 1 | .nuxt 2 | -------------------------------------------------------------------------------- /examples/with-nuxtjs/index.js: -------------------------------------------------------------------------------- 1 | const polka = require('polka'); 2 | const { Nuxt, Builder } = require('nuxt'); 3 | 4 | const { PORT=3000, NODE_ENV } = process.env; 5 | 6 | const dev = NODE_ENV !== 'production'; 7 | const nuxt = new Nuxt({ dev }); 8 | const app = polka(); 9 | 10 | // Render every route with Nuxt.js 11 | app.use(nuxt.render); 12 | 13 | // Build only in dev mode with hot-reloading 14 | if (dev) { 15 | new Builder(nuxt).build() 16 | .then(listen) 17 | .catch((error) => { 18 | console.error(error); 19 | process.exit(1); 20 | }) 21 | } else { 22 | listen(); 23 | } 24 | 25 | function listen() { 26 | return app.listen(PORT, () => { 27 | console.log(`> Ready on localhost:${PORT}`); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /examples/with-nuxtjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "nuxt build", 4 | "prestart": "npm run build", 5 | "start": "cross-env NODE_ENV=production node index", 6 | "watch": "node index" 7 | }, 8 | "dependencies": { 9 | "cross-env": "^5.1.3", 10 | "nuxt": "latest", 11 | "polka": "latest" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/with-nuxtjs/pages/about.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /examples/with-nuxtjs/pages/index.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /examples/with-sapper/.gitignore: -------------------------------------------------------------------------------- 1 | .sapper 2 | templates/.* 3 | -------------------------------------------------------------------------------- /examples/with-sapper/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukeed/polka/c36cf8b5d1a57164e64fdd7d4e0d0f80ab262dc0/examples/with-sapper/assets/favicon.png -------------------------------------------------------------------------------- /examples/with-sapper/assets/global.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: Roboto, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 4 | font-size: 14px; 5 | line-height: 1.5; 6 | color: #333; 7 | } 8 | 9 | main { 10 | position: relative; 11 | max-width: 56em; 12 | background-color: white; 13 | padding: 2em; 14 | margin: 0 auto; 15 | box-sizing: border-box; 16 | } 17 | 18 | h1, h2, h3, h4, h5, h6 { 19 | margin: 0 0 0.5em 0; 20 | font-weight: 400; 21 | line-height: 1.2; 22 | } 23 | 24 | h1 { 25 | font-size: 2em; 26 | } 27 | 28 | a { 29 | color: inherit; 30 | } 31 | 32 | code { 33 | font-family: menlo, inconsolata, monospace; 34 | font-size: calc(1em - 2px); 35 | color: #555; 36 | background-color: #f0f0f0; 37 | padding: 0.2em 0.4em; 38 | border-radius: 2px; 39 | } 40 | 41 | @media (min-width: 400px) { 42 | body { 43 | font-size: 16px; 44 | } 45 | } -------------------------------------------------------------------------------- /examples/with-sapper/assets/great-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukeed/polka/c36cf8b5d1a57164e64fdd7d4e0d0f80ab262dc0/examples/with-sapper/assets/great-success.png -------------------------------------------------------------------------------- /examples/with-sapper/assets/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "background_color": "#ffffff", 3 | "theme_color": "#aa1e1e", 4 | "name": "TODO", 5 | "short_name": "TODO", 6 | "display": "minimal-ui", 7 | "start_url": "/", 8 | "icons": [ 9 | { 10 | "src": "svelte-logo-192.png", 11 | "sizes": "192x192", 12 | "type": "image/png" 13 | }, 14 | { 15 | "src": "svelte-logo-512.png", 16 | "sizes": "512x512", 17 | "type": "image/png" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /examples/with-sapper/assets/svelte-logo-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukeed/polka/c36cf8b5d1a57164e64fdd7d4e0d0f80ab262dc0/examples/with-sapper/assets/svelte-logo-192.png -------------------------------------------------------------------------------- /examples/with-sapper/assets/svelte-logo-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukeed/polka/c36cf8b5d1a57164e64fdd7d4e0d0f80ab262dc0/examples/with-sapper/assets/svelte-logo-512.png -------------------------------------------------------------------------------- /examples/with-sapper/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "sapper build", 4 | "start": "cross-env NODE_ENV=production node server.js", 5 | "watch": "node server.js" 6 | }, 7 | "dependencies": { 8 | "compression": "^1.7.1", 9 | "cross-env": "^5.1.3", 10 | "node-fetch": "^2.6.1", 11 | "polka": "latest", 12 | "sapper": "^0.6.1", 13 | "serve-static": "^1.13.1", 14 | "svelte": "^1.51.1" 15 | }, 16 | "devDependencies": { 17 | "css-loader": "^0.28.7", 18 | "extract-text-webpack-plugin": "^3.0.2", 19 | "style-loader": "^0.19.1", 20 | "svelte-loader": "^2.3.3", 21 | "uglifyjs-webpack-plugin": "^1.1.5", 22 | "webpack": "^3.10.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/with-sapper/readme.md: -------------------------------------------------------------------------------- 1 | # Example: Sapper 2 | 3 | > Polka and Sapper make for an exceptional combo! :tada: _([DEMO](https://polka-sapper.now.sh/))_ 4 | 5 | [Sapper](https://github.com/sveltejs/sapper) is a structure-dependent framework that all allows you to build highly-performant, universal [Svelte](https://github.com/sveltejs/svelte) apps! Sapper operates as a middleware layer, which allows you to attach it to _any_ Node.js server. 6 | 7 | This example shows how easy it is to use Polka with the default [`sapper-template`](https://github.com/sveltejs/sapper-template). 8 | 9 | ## Setup 10 | 11 | ```sh 12 | $ npm install 13 | $ npm run build 14 | $ npm start 15 | ``` 16 | 17 | ## Usage 18 | 19 | Open a browser to `localhost:3000`! 20 | 21 | To use the built-in live-reload / [HMR](https://webpack.js.org/concepts/hot-module-replacement/) development server, run: 22 | 23 | ```sh 24 | $ npm run watch 25 | ``` 26 | 27 | > The dev-server also runs on Polka! :dancers: 28 | -------------------------------------------------------------------------------- /examples/with-sapper/routes/_components/Layout.html: -------------------------------------------------------------------------------- 1 |