config
For this answer we’ll establish a simple config
object to store any values –
// config.json
{"counter":0}
server
We will create a simple server using http.createServer. We will use the request method and request URL to look up a handler
or respond with 404 when no handler is found –
// server.js
import { createServer } from "http"
import { readFile, writeFile } from "fs/promises"
const server = createServer(async (req, res) => {
const handler = routes?.[req.method.toLowerCase()]?.[req.url]
if (handler == null) {
res.writeHead(404, {'Content-Type': 'text/plain'})
res.end(`No route for ${req.method} ${req.url}`)
}
else {
await handler(req, res)
res.end()
}
})
server.listen(8000)
Next we define the routes
to /getConfig
and /saveConfig
–
// server.js (continued)
const routes = {
get: {
"/getConfig": async (req, res) => {
res.writeHead(200, {'content-type': 'application/json'})
res.write(await readFile("./config.json"))
}
},
post: {
"/saveConfig": async (req, res) => {
await writeFile("./config.json", await readBody(req))
res.writeHead(204)
},
"/reset": async (req, res) => {
await writeFile("./config.json", JSON.stringify({ counter: 0 }))
res.writeHead(204)
}
}
}
This depends on a reusable helper, readBody
–
// server.js (continued)
function readBody(req) {
return new Promise((resolve, reject) => {
const body = []
req.on('data', chunk => body.push(Buffer.from(chunk)))
req.on('end', _ => resolve(Buffer.concat(body).toString()))
req.on('error', reject)
})
}
client
In this case your bot is the http client. The node docs for http.get include this long-winded example, but don’t let it worry you –
// example from node docs
http.get('http://localhost:8000/', (res) => {
const { statusCode } = res;
const contentType = res.headers['content-type'];
let error;
// Any 2xx status code signals a successful response but
// here we're only checking for 200.
if (statusCode !== 200) {
error = new Error('Request Failed.\n' +
`Status Code: ${statusCode}`);
} else if (!/^application\/json/.test(contentType)) {
error = new Error('Invalid content-type.\n' +
`Expected application/json but received ${contentType}`);
}
if (error) {
console.error(error.message);
// Consume response data to free up memory
res.resume();
return;
}
res.setEncoding('utf8');
let rawData="";
res.on('data', (chunk) => { rawData += chunk; });
res.on('end', () => {
try {
const parsedData = JSON.parse(rawData);
console.log(parsedData);
} catch (e) {
console.error(e.message);
}
});
}).on('error', (e) => {
console.error(`Got error: ${e.message}`);
});
You’re not expected to copy this verbatim. Imagine writing that much code each time you wanted to fetch some JSON. You can think of the http module as a low-level API that enables you to design higher-level functions –
// client.js
import * as http from "http"
function request (href, { body = "", ...options } = {}) {
return new Promise((resolve, reject) =>
http.request(href, options, res => {
const data = []
res.on('data', chunk => data.push(chunk))
res.on('end', _ => resolve({
status: res.statusCode,
headers: res.headers,
data: Buffer.concat(data).toString()
}))
})
.on('error', reject)
.end(body)
)
}
Above our request
function resolves a { status, headers, data }
object, and we can write specialized forms get
and getJson
that make it even easier to intereact with –
// client.js (continued)
async function get (href) {
const { status, headers, data } = await request(href)
if (status < 200 || status >= 300)
throw Error(status)
return { status, headers, data }
}
async function getJson (href) {
const { headers, data } = await get(href)
if (!headers['content-type'].startsWith("application/json"))
throw Error(`expected application/json but received ${headers['content-type']}`)
return JSON.parse(data)
}
We can do the same for post
–
// client.js (continued)
async function post (href, body = "") {
const { status, headers, data } = await request(href, { body, method: "POST" })
if (status < 200 || status >= 300)
throw Error(status)
return { status, headers, data }
}
Finally here is our bot
code. It reads the config via get
, updates the config via post
, and re-reads it via get
to return the confirmed result –
// client.js (continued)
async function bot() {
const config = await getJson("http://localhost:8000/getConfig")
await post("http://localhost:8000/saveConfig", JSON.stringify({counter: config.counter + 1}))
return getJson("http://localhost:8000/getConfig")
}
bot().then(console.log, console.error)
run
Start the server
in your terminal –
$ node ./server.js
In a separate terminal, run the client
a few times –
$ node ./client.js
{ counter: 1 }
$ node ./client.js
{ counter: 2 }
$ node ./client.js
{ counter: 3 }
node modules
Above we took a sort of DIY approach to the problem. But this kind of problem has been solved many ways before. There are popular libraries like express and koajs that would make much of this a lot easier. Now that you know the purpose they serve, give ’em a try!
solved NODE.JS How do I save JSON data without filling up my storage [closed]