Welcome to Frameworkless.js

(boring) details

Who is the course for? | How long will it take? | Other stuff...
What are we going to Learn?
π https://remind.ist
Email future you, today...
...aka schedule an email to yourself in the future to remind you about something.
- Build your product idea β fast & leanβ’
- Get framework learning out of your critical path
- End up with a solid starter project you can reference or adapt to any other idea you want
We will build a real product together:
Well then, let's jump right in...
$ mkdir remind.ist && cd remind.ist $ npm init
Don't have Node.js installed? Get it through Node Version Manager (nvm):
https://github.com/nvm-sh/nvm#installation-and-update
The simplest Node.js app ever...
// app.js const run = () => { console.log('Run, pump those crazy legs!') } run()
...and this course is over, thanks for watching!
Joking, obviously π
$ node app.js Run, pump those crazy legs! $
Auto-restart on changes
$ npm install nodemon --save-dev
// package.json { ... "scripts": { "start": "nodemon --inspect -e js,html,hbs,sql,css,scss app.js" } ... }
More about Nodemon:
$ npm run start
Environment variables
// app.js require('dotenv').config() const { createServer } = require('http') const PORT = process.env.PORT || 1234 const server = createServer((request, response) => { return response.end('Look, this is the response!') }) server.listen(PORT, () => { console.log(`We are running the server on port ${PORT}`) })
// app.js require('dotenv').config() const { createServer } = require('http') const PORT = process.env.PORT || 1234 const server = createServer((request, response) => { return response.end('Look, this is the response!') }) server.listen(PORT, () => { console.log(`We are running the server on port ${PORT}`) })
# .env PORT=1234
$ npm install dotenv --save-dev
More about Dotenv:
Let's do something useful though...
// app.js const { createServer } = require('http') const PORT = 1234 const server = createServer((request, response) => { return response.end('Look, this is the response!') }) server.listen(PORT, () => { console.log(`We are running the server on port ${PORT}`) })
// app.js const { createServer } = require('http') const PORT = 1234 const server = createServer((request, response) => { return response.end('Look, this is the response!') }) server.listen(PORT, () => { console.log(`We are running the server on port ${PORT}`) })
// app.js const { createServer } = require('http') const PORT = 1234 const server = createServer((request, response) => { return response.end('Look, this is the response!') }) server.listen(PORT, () => { console.log(`We are running the server on port ${PORT}`) })
Documentation for Node's standard HTTP library:
Time to reflect on your work

You did awesome! ππ
HTTP server in only a couple lines of code!
We'll call it...responder...I guess

New files...new files everywhere
- Move HTTP server to a new file, initialisers/http.js
- Create a public folder to serve static files from
- Create a lib/responder.js library we will utilise to serve various types of content (but for now just static files)
- Create a config/constants.js file to store customisable pieces in a central place

Whitelisting extensions
// config/constants.js exports.STATIC_EXTENSIONS = [ 'jpg', 'jpeg', 'gif', 'png', 'svg', 'ico', 'css', 'js', 'xml', 'webmanifest', 'txt', 'html', 'eot', 'ttf', 'woff', ]
And now we touch the file system
// lib/responder.js const { open } = require('fs').promises const { STATIC_EXTENSIONS } = require('../config/constants') const serveStaticFile = async ({ file, extension, statusCode }, response) => { if (STATIC_EXTENSIONS.indexOf(extension) === -1) throw new Error('not_found') let fileHandle try { fileHandle = await open(`./public/${file}`, 'r') const staticFile = await fileHandle.readFile() return response.end(staticFile) } catch (error) { console.error(error) throw new Error('not_found') } finally { if (fileHandle) fileHandle.close() } } module.exports = serveStaticFile
// lib/responder.js const { open } = require('fs').promises const { STATIC_EXTENSIONS } = require('../config/constants') const serveStaticFile = async ({ file, extension, statusCode }, response) => { if (STATIC_EXTENSIONS.indexOf(extension) === -1) throw new Error('not_found') let fileHandle try { fileHandle = await open(`./public/${file}`, 'r') const staticFile = await fileHandle.readFile() return response.end(staticFile) } catch (error) { console.error(error) throw new Error('not_found') } finally { if (fileHandle) fileHandle.close() } } module.exports = serveStaticFile
// lib/responder.js const { open } = require('fs').promises const { STATIC_EXTENSIONS } = require('../config/constants') const serveStaticFile = async ({ file, extension, statusCode }, response) => { if (STATIC_EXTENSIONS.indexOf(extension) === -1) throw new Error('not_found') let fileHandle try { fileHandle = await open(`./public/${file}`, 'r') const staticFile = await fileHandle.readFile() return response.end(staticFile) } catch (error) { console.error(error) throw new Error('not_found') } finally { if (fileHandle) fileHandle.close() } } module.exports = serveStaticFile
// lib/responder.js const { open } = require('fs').promises const { STATIC_EXTENSIONS } = require('../config/constants') const serveStaticFile = async ({ file, extension, statusCode }, response) => { if (STATIC_EXTENSIONS.indexOf(extension) === -1) throw new Error('not_found') let fileHandle try { fileHandle = await open(`./public/${file}`, 'r') const staticFile = await fileHandle.readFile() return response.end(staticFile) } catch (error) { console.error(error) throw new Error('not_found') } finally { if (fileHandle) fileHandle.close() } } module.exports = serveStaticFile
// lib/responder.js const { open } = require('fs').promises const { STATIC_EXTENSIONS } = require('../config/constants') const serveStaticFile = async ({ file, extension, statusCode }, response) => { if (STATIC_EXTENSIONS.indexOf(extension) === -1) throw new Error('not_found') let fileHandle try { fileHandle = await open(`./public/${file}`, 'r') const staticFile = await fileHandle.readFile() return response.end(staticFile) } catch (error) { console.error(error) throw new Error('not_found') } finally { if (fileHandle) fileHandle.close() } } module.exports = serveStaticFile
// lib/responder.js const { open } = require('fs').promises const { STATIC_EXTENSIONS } = require('../config/constants') const serveStaticFile = async ({ file, extension, statusCode }, response) => { if (STATIC_EXTENSIONS.indexOf(extension) === -1) throw new Error('not_found') let fileHandle try { fileHandle = await open(`./public/${file}`, 'r') const staticFile = await fileHandle.readFile() return response.end(staticFile) } catch (error) { console.error(error) throw new Error('not_found') } finally { if (fileHandle) fileHandle.close() } } module.exports = serveStaticFile
// lib/responder.js const { open } = require('fs').promises const { STATIC_EXTENSIONS } = require('../config/constants') const serveStaticFile = async ({ file, extension, statusCode }, response) => { if (STATIC_EXTENSIONS.indexOf(extension) === -1) throw new Error('not_found') let fileHandle try { fileHandle = await open(`./public/${file}`, 'r') const staticFile = await fileHandle.readFile() return response.end(staticFile) } catch (error) { console.error(error) throw new Error('not_found') } finally { if (fileHandle) fileHandle.close() } } module.exports = serveStaticFile
// lib/responder.js const { open } = require('fs').promises const { STATIC_EXTENSIONS } = require('../config/constants') const serveStaticFile = async ({ file, extension, statusCode }, response) => { if (STATIC_EXTENSIONS.indexOf(extension) === -1) throw new Error('not_found') let fileHandle try { fileHandle = await open(`./public/${file}`, 'r') const staticFile = await fileHandle.readFile() return response.end(staticFile) } catch (error) { console.error(error) throw new Error('not_found') } finally { if (fileHandle) fileHandle.close() } } module.exports = serveStaticFile
Building in the responder
// initialisers/http.js if (!process.env.PORT) require('dotenv').config() const { createServer } = require('http') const serveStaticFile = require('../lib/responder') const { PORT, APP_NAME } = process.env module.exports = () => { const server = createServer(async ({ url }, response) => { const urlTokens = url.split('.') const extension = urlTokens.length > 1 ? `${urlTokens[urlTokens.length - 1].toLowerCase().trim()}` : false const isRoot = [ '', '/' ].indexOf(url) > -1 const path = isRoot ? '/index.html' : url try { return await serveStaticFile({ file: path, extension: isRoot ? 'html' : extension }, response) } catch (error) { console.error(error) return await serveStaticFile({ file: '/error.html', extension: 'html', statusCode: 500 }, response) } }) server.on('request', ({ method, url }) => { const now = new Date() console.info(`=> ${now.toUTCString()} β ${method} ${url}`) }) server.listen(PORT, () => { console.log(`=> ${APP_NAME} running on port ${PORT}`) }) }
// initialisers/http.js if (!process.env.PORT) require('dotenv').config() const { createServer } = require('http') const serveStaticFile = require('../lib/responder') const { PORT, APP_NAME } = process.env module.exports = () => { const server = createServer(async ({ url }, response) => { const urlTokens = url.split('.') const extension = urlTokens.length > 1 ? `${urlTokens[urlTokens.length - 1].toLowerCase().trim()}` : false const isRoot = [ '', '/' ].indexOf(url) > -1 const path = isRoot ? '/index.html' : url try { return await serveStaticFile({ file: path, extension: isRoot ? 'html' : extension }, response) } catch (error) { console.error(error) return await serveStaticFile({ file: '/error.html', extension: 'html', statusCode: 500 }, response) } }) server.on('request', ({ method, url }) => { const now = new Date() console.info(`=> ${now.toUTCString()} β ${method} ${url}`) }) server.listen(PORT, () => { console.log(`=> ${APP_NAME} running on port ${PORT}`) }) }
// initialisers/http.js if (!process.env.PORT) require('dotenv').config() const { createServer } = require('http') const serveStaticFile = require('../lib/responder') const { PORT, APP_NAME } = process.env module.exports = () => { const server = createServer(async ({ url }, response) => { const urlTokens = url.split('.') const extension = urlTokens.length > 1 ? `${urlTokens[urlTokens.length - 1].toLowerCase().trim()}` : false const isRoot = [ '', '/' ].indexOf(url) > -1 const path = isRoot ? '/index.html' : url try { return await serveStaticFile({ file: path, extension: isRoot ? 'html' : extension }, response) } catch (error) { console.error(error) return await serveStaticFile({ file: '/error.html', extension: 'html', statusCode: 500 }, response) } }) server.on('request', ({ method, url }) => { const now = new Date() console.info(`=> ${now.toUTCString()} β ${method} ${url}`) }) server.listen(PORT, () => { console.log(`=> ${APP_NAME} running on port ${PORT}`) }) }
// initialisers/http.js if (!process.env.PORT) require('dotenv').config() const { createServer } = require('http') const serveStaticFile = require('../lib/responder') const { PORT, APP_NAME } = process.env module.exports = () => { const server = createServer(async ({ url }, response) => { const urlTokens = url.split('.') const extension = urlTokens.length > 1 ? `${urlTokens[urlTokens.length - 1].toLowerCase().trim()}` : false const isRoot = [ '', '/' ].indexOf(url) > -1 const path = isRoot ? '/index.html' : url try { return await serveStaticFile({ file: path, extension: isRoot ? 'html' : extension }, response) } catch (error) { console.error(error) return await serveStaticFile({ file: '/error.html', extension: 'html', statusCode: 500 }, response) } }) server.on('request', ({ method, url }) => { const now = new Date() console.info(`=> ${now.toUTCString()} β ${method} ${url}`) }) server.listen(PORT, () => { console.log(`=> ${APP_NAME} running on port ${PORT}`) }) }
// initialisers/http.js if (!process.env.PORT) require('dotenv').config() const { createServer } = require('http') const serveStaticFile = require('../lib/responder') const { PORT, APP_NAME } = process.env module.exports = () => { const server = createServer(async ({ url }, response) => { const urlTokens = url.split('.') const extension = urlTokens.length > 1 ? `${urlTokens[urlTokens.length - 1].toLowerCase().trim()}` : false const isRoot = [ '', '/' ].indexOf(url) > -1 const path = isRoot ? '/index.html' : url try { return await serveStaticFile({ file: path, extension: isRoot ? 'html' : extension }, response) } catch (error) { console.error(error) return await serveStaticFile({ file: '/error.html', extension: 'html', statusCode: 500 }, response) } }) server.on('request', ({ method, url }) => { const now = new Date() console.info(`=> ${now.toUTCString()} β ${method} ${url}`) }) server.listen(PORT, () => { console.log(`=> ${APP_NAME} running on port ${PORT}`) }) }
// initialisers/http.js if (!process.env.PORT) require('dotenv').config() const { createServer } = require('http') const serveStaticFile = require('../lib/responder') const { PORT, APP_NAME } = process.env module.exports = () => { const server = createServer(async ({ url }, response) => { const urlTokens = url.split('.') const extension = urlTokens.length > 1 ? `${urlTokens[urlTokens.length - 1].toLowerCase().trim()}` : false const isRoot = [ '', '/' ].indexOf(url) > -1 const path = isRoot ? '/index.html' : url try { return await serveStaticFile({ file: path, extension: isRoot ? 'html' : extension }, response) } catch (error) { console.error(error) return await serveStaticFile({ file: '/error.html', extension: 'html', statusCode: 500 }, response) } }) server.on('request', ({ method, url }) => { const now = new Date() console.info(`=> ${now.toUTCString()} β ${method} ${url}`) }) server.listen(PORT, () => { console.log(`=> ${APP_NAME} running on port ${PORT}`) }) }
// initialisers/http.js if (!process.env.PORT) require('dotenv').config() const { createServer } = require('http') const serveStaticFile = require('../lib/responder') const { PORT, APP_NAME } = process.env module.exports = () => { const server = createServer(async ({ url }, response) => { const urlTokens = url.split('.') const extension = urlTokens.length > 1 ? `${urlTokens[urlTokens.length - 1].toLowerCase().trim()}` : false const isRoot = [ '', '/' ].indexOf(url) > -1 const path = isRoot ? '/index.html' : url try { return await serveStaticFile({ file: path, extension: isRoot ? 'html' : extension }, response) } catch (error) { console.error(error) return await serveStaticFile({ file: '/error.html', extension: 'html', statusCode: 500 }, response) } }) server.on('request', ({ method, url }) => { const now = new Date() console.info(`=> ${now.toUTCString()} β ${method} ${url}`) }) server.listen(PORT, () => { console.log(`=> ${APP_NAME} running on port ${PORT}`) }) }
// initialisers/http.js if (!process.env.PORT) require('dotenv').config() const { createServer } = require('http') const serveStaticFile = require('../lib/responder') const { PORT, APP_NAME } = process.env module.exports = () => { const server = createServer(async ({ url }, response) => { const urlTokens = url.split('.') const extension = urlTokens.length > 1 ? `${urlTokens[urlTokens.length - 1].toLowerCase().trim()}` : false const isRoot = [ '', '/' ].indexOf(url) > -1 const path = isRoot ? '/index.html' : url try { return await serveStaticFile({ file: path, extension: isRoot ? 'html' : extension }, response) } catch (error) { console.error(error) return await serveStaticFile({ file: '/error.html', extension: 'html', statusCode: 500 }, response) } }) server.on('request', ({ method, url }) => { const now = new Date() console.info(`=> ${now.toUTCString()} β ${method} ${url}`) }) server.listen(PORT, () => { console.log(`=> ${APP_NAME} running on port ${PORT}`) }) }
// initialisers/http.js if (!process.env.PORT) require('dotenv').config() const { createServer } = require('http') const serveStaticFile = require('../lib/responder') const { PORT, APP_NAME } = process.env module.exports = () => { const server = createServer(async ({ url }, response) => { const urlTokens = url.split('.') const extension = urlTokens.length > 1 ? `${urlTokens[urlTokens.length - 1].toLowerCase().trim()}` : false const isRoot = [ '', '/' ].indexOf(url) > -1 const path = isRoot ? '/index.html' : url try { return await serveStaticFile({ file: path, extension: isRoot ? 'html' : extension }, response) } catch (error) { console.error(error) return await serveStaticFile({ file: '/error.html', extension: 'html', statusCode: 500 }, response) } }) server.on('request', ({ method, url }) => { const now = new Date() console.info(`=> ${now.toUTCString()} β ${method} ${url}`) }) server.listen(PORT, () => { console.log(`=> ${APP_NAME} running on port ${PORT}`) }) }
// initialisers/http.js if (!process.env.PORT) require('dotenv').config() const { createServer } = require('http') const serveStaticFile = require('../lib/responder') const { PORT, APP_NAME } = process.env module.exports = () => { const server = createServer(async ({ url }, response) => { const urlTokens = url.split('.') const extension = urlTokens.length > 1 ? `${urlTokens[urlTokens.length - 1].toLowerCase().trim()}` : false const isRoot = [ '', '/' ].indexOf(url) > -1 const path = isRoot ? '/index.html' : url try { return await serveStaticFile({ file: path, extension: isRoot ? 'html' : extension }, response) } catch (error) { console.error(error) return await serveStaticFile({ file: '/error.html', extension: 'html', statusCode: 500 }, response) } }) server.on('request', ({ method, url }) => { const now = new Date() console.info(`=> ${now.toUTCString()} β ${method} ${url}`) }) server.listen(PORT, () => { console.log(`=> ${APP_NAME} running on port ${PORT}`) }) }
// initialisers/http.js if (!process.env.PORT) require('dotenv').config() const { createServer } = require('http') const serveStaticFile = require('../lib/responder') const { PORT, APP_NAME } = process.env module.exports = () => { const server = createServer(async ({ url }, response) => { const urlTokens = url.split('.') const extension = urlTokens.length > 1 ? `${urlTokens[urlTokens.length - 1].toLowerCase().trim()}` : false const isRoot = [ '', '/' ].indexOf(url) > -1 const path = isRoot ? '/index.html' : url try { return await serveStaticFile({ file: path, extension: isRoot ? 'html' : extension }, response) } catch (error) { console.error(error) return await serveStaticFile({ file: '/error.html', extension: 'html', statusCode: 500 }, response) } }) server.on('request', ({ method, url }) => { const now = new Date() console.info(`=> ${now.toUTCString()} β ${method} ${url}`) }) server.listen(PORT, () => { console.log(`=> ${APP_NAME} running on port ${PORT}`) }) }
// initialisers/http.js if (!process.env.PORT) require('dotenv').config() const { createServer } = require('http') const serveStaticFile = require('../lib/responder') const { PORT, APP_NAME } = process.env module.exports = () => { const server = createServer(async ({ url }, response) => { const urlTokens = url.split('.') const extension = urlTokens.length > 1 ? `${urlTokens[urlTokens.length - 1].toLowerCase().trim()}` : false const isRoot = [ '', '/' ].indexOf(url) > -1 const path = isRoot ? '/index.html' : url try { return await serveStaticFile({ file: path, extension: isRoot ? 'html' : extension }, response) } catch (error) { console.error(error) return await serveStaticFile({ file: '/error.html', extension: 'html', statusCode: 500 }, response) } }) server.on('request', ({ method, url }) => { const now = new Date() console.info(`=> ${now.toUTCString()} β ${method} ${url}`) }) server.listen(PORT, () => { console.log(`=> ${APP_NAME} running on port ${PORT}`) }) }
Mimes! No, not the performers...
// lib/responder.js // line 2, at the top: const { lookup } = require('mime-types') // line 15, inside serveStaticFile's try block: const mime = lookup(extension) if (!mime) throw new Error('not_found') response.writeHead(statusCode || 200, { 'Content-Type': mime })
// lib/responder.js // line 2, at the top: const { lookup } = require('mime-types') // line 15, inside serveStaticFile's try block: const mime = lookup(extension) if (!mime) throw new Error('not_found') response.writeHead(statusCode || 200, { 'Content-Type': mime })
// lib/responder.js // line 2, at the top: const { lookup } = require('mime-types') // line 15, inside serveStaticFile's try block: const mime = lookup(extension) if (!mime) throw new Error('not_found') response.writeHead(statusCode || 200, { 'Content-Type': mime })
$ npm install mime-types --save
More about mime-types:
Well done, you made it! πππ

See you next time where we look at Dynamic content and templating.
Please don't hesitate to contact me or leave feedback on the course:
Telegram channel: https://t.me/frameworkless
Twitter: @mtimofiiv
See today's code: https://github.com/frameworkless-js/remind.ist/tree/stage/1
See today's lesson running live: https://part1.remind.ist
frameworkless.js -> Part 1
By Mike Timofiiv
frameworkless.js -> Part 1
Check out https://frameworkless.js.org/course/1 for the whole presentation
- 1,948