create-redis-key
TypeScript icon, indicating that this package has built-in type declarations

1.0.1 • Public • Published

Create Redis Key

A Redis key creation utility.

Create Redis Key Templates, which include parameters, using a nested config object & use your Redis Key Template strings to create Redis Keys.

This package heavily uses Template Literal Types which is available since TypeScript 4.1 so you need at least this version of Typescript for this package to properly work.

npm MIT License "Buy Me A Coffee"

Examples

See it on action (StackBlitz)

Check How to Use section to see explanations of usage options on examples.

Sections

Installation

Install create-redis-key with npm

  npm install create-redis-key

Type definitions? Included!

How It Works

Eventual purpose of this library is to create a Redis Key (which is basically a string) using a template which we call in this library a Redis Key Template.

There is a function called createRedisKey() which takes a Redis Key Template and an object which includes values for the params in the Redis Key Template, then replaces parameters in template with the given values.

Most basic usage is as follows:

const blogPostRK = createRedisKey('posts:%PostID%', {
	PostID: '1',
});

This creates a string which equals to posts:1

There are 3 ways you can use this library.

  • Create a Redis Key Templates Map using createRedisKeysMap() to use it in conjunction with createRedisKey() to create Redis keys.
  • Create an object which has keys shaped as a Redis Key Template and use it in conjunction with createRedisKey() to create Redis keys.
  • Just use createRedisKey() function by writing your Redis Key Template as parameter to create a Redis key.

There are detailed explanations for each of them down below.

Usage

There are 3 ways you can use this library. Examples for different options show how you can get the same output using different methods.

You will get parameter suggestions on your IDE based on the Redis Key Template you provided to createRedisKey() function.

All params on a Redis Key Template are required. You will get type errors if you don't provide all of them.

First of all, import needed functions as follows:

import {
	createRedisKeyParam,
	createRedisKeysMap,
	createRedisKey,
} from 'create-redis-key';

or using require

var CRK = require('create-redis-key');

const { createRedisKeyParam, createRedisKeysMap, createRedisKey } = CRK;

Option 1 (Recommended)

Create a Redis Keys Config object.

You should write as const at the end of the object for things to properly work.

const redisKeysConfig = {
	SCOPE_FIRST_PART: [],

	appStatus: ['app-status'],

	restaurants: {
		SCOPE_FIRST_PART: ['RESTAURANTS'],
		byCategory: ['by-category', createRedisKeyParam('CategoryID')],
		byCity: [createRedisKeyParam('CityID')],
	},

	categories: {
		SCOPE_FIRST_PART: ['categories'],
		byID: [createRedisKeyParam('CategoryID')],
	},

	users: {
		SCOPE_FIRST_PART: ['users'],
		online: ['online'],
		withActiveOrder: ['with-active-order'],
		byID: ['by-id', createRedisKeyParam('UserID')],
	},

	couriers: {
		SCOPE_FIRST_PART: ['couriers'],
		Online: ['online'],
		OnDelivery: ['on-delivery'],
		byID: {
			SCOPE_FIRST_PART: ['by-id', createRedisKeyParam('CourierID')],
			PreviousDeliveries: ['previous-deliveries'],
		},
	},

	orders: {
		SCOPE_FIRST_PART: ['orders'],
		byUser: ['of-user', createRedisKeyParam('UserID')],
		byCity: {
			SCOPE_FIRST_PART: ['by-city', createRedisKeyParam('CityName')],
			byCourier: ['of-courier', createRedisKeyParam('CourierID')],
		},
	},
} as const;

Then create a Redis Keys Templates Map using the config:

If you give an invalid config, return type will be never. I explained why it works this way at FAQ section.

const RedisKeysMap = createRedisKeysMap(exampleRedisKeysConfig);

It will create a Redis Keys Templates Map

{
  appStatus: 'app-status',
  restaurants: {
    byCategory: 'RESTAURANTS:by-category:%CategoryID%',
    byCity: 'RESTAURANTS:%CityID%',
  },
  categories: {
    byID: 'categories:%CategoryID%',
  },
  users: {
    online: 'users:online',
    withActiveOrder: 'users:with-active-order',
    byID: 'users:by-id:%UserID%',
  },
  couriers: {
    Online: 'couriers:online',
    OnDelivery: 'couriers:on-delivery',
    byID: {
      PreviousDeliveries: 'couriers:by-id:%CourierID%:previous-deliveries',
    },
  },
  orders: {
    byUser: 'orders:of-user:%UserID%',
    byCity: {
      byCourier: 'orders:by-city:%CityName%:of-courier:%CourierID%',
    },
  },
}

You can then use this map to create a Redis key when needed:

This will produce couriers:by-id:1234:previous-deliveries

const previousDeliveriesOfCourierRK = createRedisKey(
	RedisKeysMap.couriers.byID.PreviousDeliveries,
	{
		CourierID: '1234',
	}
);

Create another key using map:

This will produce orders:by-city:istanbul:of-courier:1234

const latestOrdersOfCourierInCityRK = createRedisKey(
	RedisKeysMap.orders.byCity.byCourier,
	{
		CourierID: '1234',
		CityName: 'istanbul',
	}
);

Option 2

Instead of creating a Redis Keys Templates Map using createRedisKeysMap() with a config, you can write it yourself.

You should write as const at the end of the object for things to properly work.

When you write Redis Key Templates manually, be aware that it is much more error prone than using Option 1.

const DeliveryServiceRedisKeyTemplatesMap = {
	appStatus: 'app-status',
	restaurantsByCategory: 'RESTAURANTS:by-category:%CategoryID%',
	users: 'users:with-active-order',
	previousDeliveriesOfCourier: 'couriers:by-id:%CourierID%:previous-deliveries',
	latestOrdersOfCourierInCity:
		'orders:by-city:%CityName%:of-courier:%CourierID%',
} as const;

Then you can use it just like shown on Option 1:

This will produce orders:by-city:istanbul:of-courier:1234

const latestOrdersOfCourierInCityRK = createRedisKey(
	DeliveryServiceRedisKeyTemplatesMap.latestOrdersOfCourierInCity,
	{
		CourierID: '1234',
		CityName: 'istanbul',
	}
);

Option 3

This is most basic usage of this package.

You can just write your Redis Key Template as a parameter:

This will produce orders:by-city:istanbul:of-courier:1234

const latestOrdersOfCourierInCityRK = createRedisKey(
	'orders:by-city:%CityName%:of-courier:%CourierID%',
	{
		CourierID: '1234',
		CityName: 'istanbul',
	}
);

Documentation

Documentation - Terms

Redis Key Template

A string to be used as a template to create a Redis key.

Format: a-key-part:%ParamName1%:another-key-part:%ParamName2%

Redis Key Param

A part of Redis Key Template which represents a variable part of the key.

Format: %ParamName%

Redis Key Part

A part of Redis Key Template which is either a Redis Key Param or a string

Formats: %ParamName% | random-text

Redis Keys Config Template Array

An array of Redis Key Part

const exampleTemplateArray = ['key1', createRedisKeyParam('Param1')];

Redis Keys Config Scope

Main building block of the a Redis Keys Config.

  • It has to have a key named SCOPE_FIRST_PART which is a Redis Keys Config Template Array
  • Other keys can be either a Redis Keys Config Template Array or a Redis Keys Config Scope
const exampleScope = {
	SCOPE_FIRST_PART: [],
	key0: ['key0'],
	key1: ['key1', createRedisKeyParam('Param1')],
	key2: ['key2', createRedisKeyParam('Param2')],
	aNestedScope: {
		SCOPE_FIRST_PART: ['a-nested-scope', createRedisKeyParam('Param3')],
		scopedKey1: ['a-key-1'],
		scopedKey2: ['a-key-2', createRedisKeyParam('KeyParam')],
	},
};

Redis Keys Config

A config object to create Redis Keys Template Map

  • This is actually a Redis Keys Config Scope
const exampleRedisKeysConfig = {
	SCOPE_FIRST_PART: [],
	key1: ['a-random-text-1', createRedisKeyParam('Param1')],
	key2: ['another-text', createRedisKeyParam('Param2')],
	aNestedScope: {
		SCOPE_FIRST_PART: ['a-nested-scope', createRedisKeyParam('Param3')],
		scopedKey1: ['a-key-1', createRedisKeyParam('KeyParam')],
	},
} as const;

Redis Keys Template Map

This is the product of createRedisKeysMap() function.

Given the following config to createRedisKeysMap() function:

const exampleRedisKeysConfig = {
	SCOPE_FIRST_PART: [],
	key1: ['a-random-text-1', createRedisKeyParam('Param1')],
	key2: ['another-text', createRedisKeyParam('Param2')],
	aNestedScope: {
		SCOPE_FIRST_PART: ['a-nested-scope', createRedisKeyParam('Param3')],
		scopedKey1: ['a-key-1', createRedisKeyParam('KeyParam')],
	},
} as const;

When you use this config to create a map:

createRedisKeysMap(exampleRedisKeysConfig);

It will produce this object which is a Redis Keys Template Map:

{
    key1: "a-random-text-1:%Param1%";
    key2: "another-text:%Param2%";
    aNestedScope: {
        scopedKey1: "a-nested-scope:%Param3%:a-key-1:%KeyParam%";
    };
}

You can then use it with createRedisKey() to create Redis keys as needed.

Documentation - Functions

createRedisKeyParam

createRedisKeyParam(paramName: string)

Creates a Redis Key Param object.

It can be used in a Redis Keys Config Template Array when creating Redis Keys Config

const exampleRedisKeysConfig = {
	SCOPE_FIRST_PART: ['micro-service', createRedisKeyParam('ServiceID')],
	key1: ['a-random-text-1', createRedisKeyParam('Param1')],
	key2: [
		'another-text',
		createRedisKeyParam('Param2'),
		'another-part',
		createRedisKeyParam('Param3'),
	],
} as const;

createRedisKeysMap

createRedisKeysMap(
  redisKeysConfig: Record<string, any>,
  optionalDelimiter: string | null
)

Creates a Redis Keys Template Map using a Redis Keys Config object.

Default delimiter is colon (:)

If you don't want to use a delimiter, give an empty string ('') to optionalDelimiter parameter.

For most cases (like 95% of them), you will use a delimiter. Therefore I chose the most commonly used one (colon :), which is also used in official Redis tutorials, as the default delimiter.

redisKeysConfig should be given as the example below. Otherwise you won't get suggestions on createRedisKey() and also Typescript will give an error when you try to provide parameter values.

readonly RedisKeysConfig does not work. Only way is to write as const at the end of the config object.

Given the config following config:

// a Redis Keys Config
const exampleRedisKeysConfig = {
	SCOPE_FIRST_PART: [],
	key1: ['a-random-text-1', createRedisKeyParam('Param1')],
	key2: ['another-text', createRedisKeyParam('Param2')],
	aNestedScope: {
		SCOPE_FIRST_PART: ['a-nested-scope', createRedisKeyParam('Param3')],
		scopedKey1: ['a-key-1', createRedisKeyParam('KeyParam')],
	},
} as const;

And called as follows:

const exampleRedisKeysTemplateMap = createRedisKeysMap(exampleRedisKeysConfig);

It will produce this object which is a Redis Keys Template Map:

{
    key1: "a-random-text-1:%Param1%";
    key2: "another-text:%Param2%";
    aNestedScope: {
        scopedKey1: "a-nested-scope:%Param3%:a-key-1:%KeyParam%";
    };
}

createRedisKey

createRedisKey(
  redisKeyTemplateString: string,
  params: Record<string, string>
): string

Creates a Redis key using a Redis Key Template and replacing parameters on template with given parameter values.

const blogPostCommentRepliesRK = createRedisKey(
	'posts:%PostID%:comments:%CommentID%:replies',
	{
		PostID: '1234',
		CommentID: '9876',
	}
);

This creates a string which equals to posts:1234:comments:9876:replies

FAQ

When I give a config object to createRedisKeysMap() it's output type is never. Why?

When you give an invalid config object to it, it returns never as a result type. Since I need your config as a readonly object, I can't make the parameter type RedisKeysConfig directly. So I need to accept an object, check if it is valid & make the return type never in order to make you aware that there is something wrong.

I now that it's a bad developer experience but I'm not sure if there is a way to solve this. Feel free to open an Issue to discuss this.

Running Tests

To run tests, run the following command:

  npm run test

Local Development

This an NPM package.

  • When you make changes,
    • Build the project.
    • Create a new empty project.
    • Link the local build to your test project.
    • Use your development version on your test project to see if it is working.
    • Write tests to verify that your new feature is working properly and also doesn't break anything.

Contributing

Contributions are always welcome!

There are some basic rules though.

  • Be sure you don't break any existing type definitions.
    • Developer Experience of this library depends on Typescript types.
    • Any change you make on type definitions might cause performance issues.
  • If you create a new Typescript type, write tests for it.
    • Yes, really. Typescript is complicated and it isn't enough if it seems like it's working.
    • Check TSD package to see how you can test types.
  • Write tests for your new feature.
    • I won't accept any PR without additional tests.
    • I won't accept any PR if it can't pass existing tests.

Authors

License

MIT

Package Sidebar

Install

npm i create-redis-key

Weekly Downloads

21

Version

1.0.1

License

MIT

Unpacked Size

35.4 kB

Total Files

21

Last publish

Collaborators

  • alperguven