[Solved] NODE.JS How do I save JSON data without filling up my storage [closed]


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]