Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/titobrian97/Prueba-tecnica-ts-node---gestion-de-csv/llms.txt

Use this file to discover all available pages before exploring further.

The backend is a single-file Express 5 server (server.ts) that exposes two REST endpoints — one for uploading and parsing a CSV file and one for searching the parsed records. It is written in TypeScript and executed directly by ts-node, so there is no separate compile step. All parsed data lives in a module-level array in memory, which means the dataset is fast to query but is discarded whenever the process restarts.

Key Dependencies

PackageVersionRole
express^5.2.1HTTP server framework — handles routing, middleware, and request/response lifecycle.
multer^2.1.1Multipart form-data middleware — receives the uploaded file and exposes its raw bytes as req.file.buffer.
convert-csv-to-json^4.36.0Converts a UTF-8 CSV string to Array<Record<string, string>> using a fluent API.
cors^2.8.6Express middleware that sets CORS headers so the Vite frontend on port 4000 can reach the API on port 3000.
cross-env^10.1.0Sets the PORT environment variable in a cross-platform way inside npm scripts.
ts-node^10.9.2Runs server.ts directly without a prior tsc compilation step.

In-Memory Data Model

The entire parsed dataset is held in a single module-level variable declared at the top of server.ts:
let usersData: Array<Record<string, string>> = [];
Each element of the array represents one CSV row as a plain object where every key is a column header and every value is a string. There is no schema enforcement — the shape of each record is determined entirely by the headers in the uploaded file. When a new CSV is uploaded the variable is fully overwritten with the freshly parsed array; there is no merging or appending of rows.
usersData is a module-level singleton. Every connected client shares the same dataset, and uploading a new CSV immediately replaces the previous data for all users. Because the variable is kept in process memory it is also lost completely whenever the Node.js process is stopped or crashes — there is no persistence layer.

Upload Middleware

multer is configured with memoryStorage so that the uploaded file is never written to disk:
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });
When upload.single("file") is applied to a route, multer reads the multipart body and attaches the result to req.file. The raw bytes of the file are accessible via req.file.buffer as a Node.js Buffer. The route handler decodes this buffer to a UTF-8 string with Buffer.from(file.buffer).toString("utf-8") before passing it to the CSV parser. Using memory storage avoids the need to manage temporary files on disk and keeps the server stateless at the OS level.

Full Server Source

import express from "express";
import cors from "cors";
import multer from "multer";
import csvToJson from "convert-csv-to-json";

const app = express();
const port = process.env.PORT ?? 3000;

const storage = multer.memoryStorage();
const upload = multer({ storage: storage });

app.use(cors());

app.listen(port, () => {
  console.log(`Server is running at http://localhost:${port}`);
});

let usersData: Array<Record<string, string>> = [];

app.post("/api/files", upload.single("file"), async (req, res) => {
  const { file } = req;

  if (!file)
    return res.status(500).json({ data: [], message: "file is required" });

  if (file.mimetype != "text/csv")
    return res
      .status(500)
      .json({ data: [], message: "El archivo debe ser un csv" });

  let json: Array<Record<string, string>> = [];

  try {
    const rawCsv = Buffer.from(file.buffer).toString("utf-8");
    console.log("rawCsv   " + rawCsv);

    json = csvToJson.fieldDelimiter(",").csvStringToJson(rawCsv);
  } catch (err) {
    console.log(err);
    return res.status(500).json({ data: [], message: "Error parsing file" });
  }

  usersData = json;

  return res
    .status(200)
    .json({ data: json, message: "El archivo se cargo correctamente" });
});

app.get("/api/users", async (req, res) => {
  const { q } = req.query;

  if (!q)
    return res.status(500).json({ message: "No hay parametro de busqueda" });

  const search = q.toString().toLowerCase();

  const filterData = usersData.filter((row) => {
    return Object.values(row).some((value) =>
      value.toLowerCase().includes(search),
    );
  });

  return res.status(200).json({ data: filterData });
});

Endpoints

Requestmultipart/form-data with a field named file.Validation
  1. Returns 500 with { data: [], message: "file is required" } if no file is present.
  2. Returns 500 with { data: [], message: "El archivo debe ser un csv" } if file.mimetype is not "text/csv".
ProcessingThe buffer is decoded to UTF-8 and parsed with:
json = csvToJson.fieldDelimiter(",").csvStringToJson(rawCsv);
The result is assigned to usersData, replacing any prior data.Success response200 OK
{
  "data": [{ "id": "1", "name": "Alice" }, { "id": "2", "name": "Bob" }],
  "message": "El archivo se cargo correctamente"
}
Query parameters
ParameterRequiredDescription
qYesSearch term. Returns 500 with { message: "No hay parametro de busqueda" } if omitted.
ProcessingThe search is case-insensitive and spans every column value in every row:
const filterData = usersData.filter((row) => {
  return Object.values(row).some((value) =>
    value.toLowerCase().includes(search),
  );
});
Success response200 OK
{
  "data": [{ "id": "1", "name": "Alice" }]
}
An empty data array is returned when no rows match — this is not treated as an error by the backend.

Environment Configuration

The server reads the port from the PORT environment variable and falls back to 3000 if it is not set:
const port = process.env.PORT ?? 3000;

Scripts

Both npm scripts use cross-env to ensure the environment variable is set consistently on Windows, macOS, and Linux:
{
  "scripts": {
    "dev":   "cross-env PORT=3000 ts-node server.ts",
    "start": "cross-env PORT=3000 ts-node server.ts"
  }
}
npm run dev and npm start are identical — both launch ts-node server.ts with PORT=3000. There is no separate watch mode or hot-reload; the process must be restarted manually after source changes.

Build docs developers (and LLMs) love