Continuing Frameworkless.js
Part 3: Forms + a bit of random stuff
Recap of Last Time
- We built a Node.js app utilising the built-in http.createServer
- Starts an HTTP server and serves static files out of the ./public directory
- We implemented dynamic routing with a handy easy to define syntax
- Our routes use Handlebars template files to display content that's not static
Pacing!
Wawaweewaa we covered a lot last time! This time, we'll go a bit slower.
Comment from last stream:
Let's talk about the debugger
const myFunction = () => {
const thisIsTruth = true
debugger
}
myFunction()
This code works great in-browser! Breakpoints are amazeballs!
BUT, getting them to work in Node.js requires one more step:
$ node --inspect test.js
Breakpoints to the rescue!
const sampleData = [
{
user_id: 1,
friends: [
{ user_id: 5 },
{ user_id: 6 }
]
}
]
> sampleData
[ { user_id: 1, friends: [ [Object], [Object] ] } ]
Not a lot of visibility into nested objects!
And now, on to forms...
HTML forms are simple:
<form action="/my_endpoint" method="POST" id="my_form" name="my_form">
<input type="text" name="my_random_text" id="my_random_text">
<button type="sumbit">Submit!</button>
</form>
And modern frontend frameworks are essentially this:
document.getElementById('my_form').addEventListener('submit', event => {
event.preventDefault()
fetch('/my_endpoint', { method: 'POST', body: { /* form field values */ } })
})
Let's parse it!
Form data can be complicated to deal with...
- Content types
- Multipart data
- File uploads
- Validation
an ultra-naive form body parser
// lib/body-parser.js
const { parse: parseFormadata } = require('querystring')
const getRequestBody = request => new Promise((resolve, reject) => {
let formData = ''
request.on('data', buffer => formData += buffer.toString())
request.on('error', reject)
request.on('end', () => {
const parsedData = parseFormadata(formData)
return resolve(parsedData)
})
})
module.exports = getRequestBody
NOTE: You probably don't want to use something like this live! It assumes the content is text, that it's not multipart and that the format is a "querystring":
my_field=wow&a_few_pi_digits=3.14159
But let's add a route into our app!
// routes/new-reminder.js
const { getRequestBody } = require('../lib/formdata')
exports.uri = '/new'
exports.template = 'new_reminder'
exports.method = 'POST'
exports.data = async request => {
const formData = await getRequestBody(request)
return formData
}
Let's create a new route:
And a template:
{{!-- templates/new_reminder.hbs --}}
<h1>Data received!</h1>
<p>Email: {{email}}</p>
<p>Message:</p>
<p>{{message}}</p>
Time to create a "hook"
// lib/responder.js
const serveRoute = async ({ request, context }, response) => {
const key = `${request.method}:${request.url}`
const route = routes[key]
if (!route) throw new Error('not_found')
Handlebars.registerPartial('content', route.body)
const hbs = Handlebars.compile(basePage)
let routeContext = {}
if (route.data) routeContext = await route.data(request)
return response.end(hbs({ ...context, ...routeContext }))
}
A request to your server has a lifecycle! So let's add a step:
Finally, let's add a form to test!
{{!-- templates/root.hbs --}}
<form action="/new" method="POST">
<div>
<input type="email" name="email" id="email" placeholder="your email"
</div>
<div>
<textarea name="message" id="message" placeholder="type your message to yourself" rows="10" cols="50"></textarea>
</div>
<div>
<button type="submit">Submit</button>
</div>
</form>
Awesome! 🤩
Form data is hard. We may have built a really simple parser but it works and all we need at the moment!
Well done, you made it! 👏👏👏
See you next time where we look at Hooking up a database to save some of that beautiful data.
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/3
See today's lesson running live: https://part3.remind.ist
frameworkless.js -> Part 3
By Mike Timofiiv
frameworkless.js -> Part 3
Check out https://frameworkless.js.org/course/2 for the whole presentation
- 764