Skip to content

Instantly share code, notes, and snippets.

@alexdwagner
Created March 9, 2024 23:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexdwagner/8c915d9571dddffadacbbd85f842fcbd to your computer and use it in GitHub Desktop.
Save alexdwagner/8c915d9571dddffadacbbd85f842fcbd to your computer and use it in GitHub Desktop.
Node.js Server for Key-Value Store and Journal Entries
/*
* Node.js Server for Key-Value Store and Journal Entries
*
* This server provides in-memory storage for key-value pairs and structured journal entries.
*
* It listens on http://localhost:4000/, storing key-value pairs sent via http://localhost:4000/set?somekey=somevalue
* and retrieving values when requested via http://localhost:4000/get?key=somekey.
*
* It supports storing and retrieving data via HTTP POST and GET requests. Key features include:
*
* - Key-Value Store: Simple storage solution for configuration settings or generic data.
* - Journal Entries: Allows saving journal entries with timestamps and tags.
* - Tag-Based Search: Retrieve journal entries based on tags.
*
* Usage:
* - POST `/set` to store key-value pairs or journal entries.
* - GET `/get` with `key`, `id`, or `tag` query parameters to retrieve data.
*
* Designed for ease of use and quick integration into development projects.
*/
const http = require('http');
const url = require('url');
let keyValueStore = {}; // Object to hold key-value pairs
// Journal entry schema
let journalEntries = {}; // Object to hold journal entries as key-value pairs
let tagsIndex = {}; // Object to map tags to entry IDs
let nextJournalId = 1; // Counter to generate unique IDs for journal entries
// Generates a unique ID for each journal entry
const generateJournalId = () => `entry-${nextJournalId++}`;
/**
* Saves a journal entry with associated tags.
* @param {string} entryText - The text of the journal entry.
* @param {Array} tags - An array of tags associated with the journal entry.
* @returns The unique ID of the saved journal entry.
*/
function saveJournalEntry(entryText, tags = []) {
const journalId = generateJournalId();
const timestamp = new Date().toISOString();
// Saving the entry as a key-value pair
journalEntries[journalId] = {
entry: entryText,
timestamp: timestamp,
tags: tags
};
// Update the tags index
updateTagsIndex(journalId, tags);
console.log(`\nJournal entry "${journalId}" saved with tags: ${tags.join(', ')}`);
return journalId; // For response purpose
}
/**
* Updates the index mapping tags to journal entry IDs.
* @param {string} entryId - The ID of the journal entry.
* @param {Array} tags - An array of tags to be indexed.
*/
function updateTagsIndex(entryId, tags) {
tags.forEach(tag => {
if (!tagsIndex[tag]) {
tagsIndex[tag] = [];
}
tagsIndex[tag].push(entryId);
});
}
/**
* Retrieves a journal entry by its ID.
* @param {string} entryId The ID of the journal entry.
* @returns The journal entry if found, or undefined.
*/
function getEntryById(entryId) {
return journalEntries[entryId];
}
/**
* Retrieves journal entries associated with a specific tag.
* @param {string} tag The tag used for filtering entries.
* @returns {Array} An array of journal entries associated with the tag.
*/
function getEntriesByTag(tag) {
const entryIds = tagsIndex[tag] || [];
return entryIds.map(id => journalEntries[id]);
}
// Server request listener
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url, true);
// Handle POST requests for setting values or journal entries
if (req.method === 'POST' && parsedUrl.pathname === '/set') {
let body = '';
req.on('data', chunk => body += chunk.toString());
req.on('end', () => {
const data = JSON.parse(body);
// Distinguish between general key-value pairs and journal entry requests based on body content
if ('key' in data && 'value' in data) {
keyValueStore[data.key] = data.value;
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'Key-value pair saved successfully' }));
} else if ('entry' in data && 'tags' in data) {
const id = saveJournalEntry(data.entry, data.tags);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'Journal entry saved successfully', id }));
} else {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Invalid request format' }));
}
});
}
// Handle GET requests for retrieving values, journal entries, or entries by tag
else if (req.method === 'GET' && parsedUrl.pathname === '/get') {
const { key, id, tag } = parsedUrl.query;
if (key) {
// Return value from key-value store
if (key in keyValueStore) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ value: keyValueStore[key] }));
} else {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Key not found' }));
}
} else if (id) {
// Return specific journal entry by id
const entry = journalEntries[id];
if (entry) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(entry));
} else {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Journal entry not found' }));
}
} else if (tag) {
// Return journal entries by tag
const entriesByTag = getEntriesByTag(tag);
if (entriesByTag.length > 0) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(entriesByTag));
} else {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'No entries found for this tag' }));
}
} else {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Invalid query parameters' }));
}
} else {
// Fallback for unsupported paths or methods
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Not Found' }));
}
});
// Start the server
const port = 4000;
server.listen(port, () => {
console.log(`\n***-Server is running on http://localhost:${port}-***`);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment