A collection of various Effectful services to communicate with 3rd party services, as well as utilities.
The modules included are used for my specific use-cases and so may be opionated, but I have attempted to make them as generic as possible nevertheless.
bun add effect-services effect
# or
npm install effect-services effect
# or
yarn add effect-services effectNote:effect is a peer dependency and must be installed separately. Check the Peer Dependencies in package.json to see which effect version is supported.
Effectful wrapper for Azure Cosmos DB NoSQL operations. Provides a type-safe interface for database and container operations with streaming support.
import*asCosmosfrom"effect-services/cosmos";Features:
- Effectful container SDK wrapper for CRUD operations
- Custom client implementation including rate limiting handling
- Query to Effect Stream support
Integration with the UK Companies House API for business information retrieval.
import*asCompaniesHousefrom"effect-services/companieshouse";Features:
- Response types provided from underlying Companies House SDK.
- Rate limit handling in CompaniesHouse
Integration with Freshservice ticketing and IT service management platform.
import*asFreshServicefrom"effect-services/freshservice";Features:
- Authentication handling
- Rate limit handling
- Automatic API pagination via Effect Stream
Integration with iManage document and content management system.
import*asiManagefrom"effect-services/imanage";Features:
- Automatic OAuth2 token refreshing
- File upload helper function
Secure secrets management using Azure Key Vault.
import*asKeyVaultfrom"effect-services/keyvault";Features:
- Secrets retrieval with optional caching
- No bootstrap secrets required
- Can be used as an Effect ConfigProvider
Integration with Legl automation platform.
import*asLeglfrom"effect-services/legl";Features:
- Automatic API pagination via Effect Stream
- Comprehensive Legl API Schemas
Comprehensive wrapper for Microsoft Graph API with streaming capabilities.
import*asGraphfrom"effect-services/msgraph";Features:
- Effectful wrapper for query construction
- Support for Graph Query to Effect Stream
Effectful MSSQL database client with query pooling and streaming.
import*asMSSQLfrom"effect-services/mssql";Features:
- Effectful connection pooling
- SQL query to Effect Stream support
- Effectful wrapper for the
mssqlSDK
Utility functions and helpers for Effect-based applications.
import*asUtilsfrom"effect-services/utils";Features:
- Task scheduling for development environments via Crons and CLI
- IP Address and CIDR parsing
- Dynamic bloom filters
- Math functions
Integration with Azure Storage File Shares.
import*asAzureFSfrom"effect-services/azurefs";Features:
- File and directory operations
- Directory items to Effect Stream helper
Important! There is no single entrypoint for this repo. When importing, use namespace imports:
import*asLeglfrom"effect-services/legl";import*asGraphfrom"effect-services/msgraph";import*asMSSQLfrom"effect-services/mssql";Not:
importLeglfrom"effect-services";// ❌ This won't workAll public APIs include JsDoc examples (WIP). Refer to the relevant function documentation for detailed usage patterns.
Interact with Microsoft 365 services including users, mail, and OneDrive:
import*asGraphfrom"effect-services/msgraph";import{Effect,Stream}from"effect";constExampleListUsers=Effect.gen(function*(){constgraph=yield*Graph.MsGraph;constrequest=yield*graph.use((c)=>c.api("/users").select(["id","displayName","mail"]).top(10));conststream=yield*Graph.MakeStream(request);yield*Stream.runForEach(stream,(user)=>Effect.log(`User: ${user.displayName} (${user.mail})`));});constLayerLive=Graph.layer({tenantID: "your-tenant-id",clientID: "your-client-id",clientSecret: "your-client-secret",scopes: ["https://graph.microsoft.com/.default"]});ExampleListUsers.pipe(Effect.provide(LayerLive),Effect.runPromise);Execute queries and handle streaming results with transaction support:
import*asMSSQLfrom"effect-services/mssql";import{Effect,Stream}from"effect";constQueryExample=Effect.gen(function*(){constsql=yield*MSSQL.MsSqlClient;// Access the connection poolconstpool=yield*sql.MakePool;constqueryString="SELECT * FROM Users WHERE Active = 1";// Simple queryconstresult=yield*Effect.tryPromise(()=>pool.query(queryString));yield*Effect.log("result:",result.output);// Streaming for large datasetsconststream=yield*sql.MakeStream(queryString);yield*Stream.runForEach(stream,(r)=>Effect.log("record:",r));});constLayerLive=MSSQL.MsSqlClient.layer({server: "your-server.database.windows.net",database: "your-database",user: "username",password: "password",// ...});QueryExample.pipe(Effect.provide(LayerLive),Effect.runPromise);Work with document collections using the Cosmos SDK:
import*asCosmosfrom"effect-services/cosmos";import{Effect,Stream}from"effect";constCosmosExample=Effect.gen(function*(){constcosmos=yield*Cosmos.CosmosClient;constcontainer=yield*cosmos.container("Users");// Container operationsconstupdateUser=yield*container.upsert({id: "123",status: "inactive",});yield*Effect.log("new status:",updateUser.resource?.status);{// Stream all items in containerconststream=yield*container.allItems;yield*Stream.runForEach(stream,(r)=>Effect.log("record ID:",r.id));}{// Stream items from queryconststream=yield*container.queryToStream({query: "SELECT * FROM c WHERE c.status = @status",parameters: [{name: "@status",value: "active"}]});yield*Stream.runForEach(stream,(u)=>Effect.log("user:",u));}// Access underlying clientsconsturl=yield*Effect.tryPromise(()=>cosmos.client.getReadEndpoint());constresponse=yield*Effect.tryPromise(()=>cosmos.database.read());});constLayerLive=Cosmos.CosmosClient.layer({connectionString: "https://your-account.documents.azure.com:443/",databaseID: "your-database-name"});CosmosExample.pipe(Effect.provide(LayerLive),Effect.runPromise);Securely retrieve and manage secrets:
import*asKeyVaultfrom"effect-services/keyvault";import{Effect,Layer}from"effect";import{DefaultAzureCredential}from"@azure/identity";constSecretExample=Effect.gen(function*(){constvault=yield*KeyVault.KeyVault;// Retrieve a secretconstdbPassword=yield*vault.getSecret("database-password");// Retrieve with cacheconstcachedVault=yield*KeyVault.KeyVaultAsCache;constapiKey=yield*cachedVault.lookup("api-key");// Use in applicationconstconnectionString=`Server=myserver;Password=${dbPassword};`;yield*Effect.log(`Connected with key version: ${apiKey.version}`);});constKeyVaultLayer=KeyVault.KeyVault.layer({vaultURL: "https://your-vault.vault.azure.us/",credential: newDefaultAzureCredential()});constCacheLayer=KeyVault.KeyVaultAsCache.layer({capacity: 10,timeToLive: "1 hour",});constLiveLayer=Layer.merge(KeyVaultLayer,CacheLayer);SecretExample.pipe(Effect.provide(LiveLayer),Effect.runPromise);Retrieve UK business information:
import*asCompaniesHousefrom"effect-services/companieshouse";import{Effect}from"effect";constCompanyLookup=Effect.gen(function*(){constclient=yield*CompaniesHouse.CompaniesHouse;constprofile=yield*client.use((c)=>c.companyProfile.getCompanyProfile("07424016")).pipe(Effect.map((r)=>r.resource));yield*Effect.log(profile?.companyName);yield*Effect.log(profile?.accounts.nextAccounts);});constLayerLive=CompaniesHouse.CompaniesHouse.layer({apiKey: "your-api-key"});CompanyLookup.pipe(Effect.provide(LayerLive),Effect.runPromise);- Bun, Node.js or equivelent runtimes.
- For versions ^2.0.0: Effect ^3.0.0 (installed separately as peer dependency)
- For versions ^3.0.0: Effect ^4.0.0 (installed separately as peer dependency)
- All other module requirements are bundled as dependencies (e.g.
@azure/identity)
All modules which use custom implementations of services will have Effectful, tagged errors:
import{Effect}from"effect";import*asMsGraphfrom"effect-services/msgraph";constMain=Effect.gen(function*(){constgraph=yield*MsGraph.MsGraph;// Effect<GraphRequest, MsGraph.MsGraphError>constquery=graph.use((c)=>c.api("/invalid/path"));});Use streaming for memory-efficient processing of large result sets:
import*asGraphfrom"effect-services/msgraph";import{Stream,Effect}from"effect";// Good: Memory-efficient streamingconstStreamUsers=Effect.gen(function*(){constgraph=yield*Graph.MsGraph;constrequest=yield*graph.use((c)=>c.api("/users"));conststream=yield*Graph.MakeStream(request);yield*Stream.runForEach(stream,(user)=>Effect.log(user));});Bun is required for development:
# Clone and install dependencies
git clone https://github.com/Shiv-SB/effect-services
cd effect-services
bun i# Build TypeScript and generate type definitions
bun run build
# This will:# - Transpile .ts to .js# - Generate .d.ts type files# - Validate exports in package.jsonsrc/ # Source TypeScript files
├── cosmos/ # Azure Cosmos DB module
├── msgraph/ # Microsoft Graph module
├── mssql/ # MSSQL database module
├── keyvault/ # Azure Key Vault module
└── ... # Other service modules
build/ # Compiled JavaScript and type definitions
lib/ # Build utilities and helpers
tests/ # Unit and integration tests
- Try to keep each module independent. Any shared functions should be stored in
internals. - The package exports are validated on build to ensure correctness
All modules use Effect's dependency injection for configuration:
import*asMSSQLfrom"effect-services/mssql";import{Effect}from"effect";constMyApp=Effect.gen(function*(){constclient=yield*MSSQL.MsSqlClient;// Use client});constlayer=MSSQL.MsSqlClient.layer({/* config */});MyApp.pipe(Effect.provide(layer),Effect.runPromise);This example showcase using three different services concurrently, mssql, msgraoh and keyvault. msgraph and mssql are used to generate Streams, and keyvault is used as the ConfigProvider.
import{DefaultAzureCredential}from"@azure/identity";import{Config,ConfigProvider,Effect,Layer,Stream}from"effect";import{MakeKeyVaultProvider}from"effect-services/keyvault";import*asMsGraphfrom"effect-services/msgraph";import*asSQLfrom"effect-services/mssql";constGetGraphUsers=Effect.gen(function*(){constgraph=yield*MsGraph.MsGraph;// .use method to access the api builderconstquery=yield*graph.use((c)=>c.api("/users").select(["id","displayName","officeLocation"]));// Pass the query to the Stream factoryconststream=yield*MsGraph.MakeStream(query);returnstream;});constGetSqlUsers=Effect.gen(function*(){constsql=yield*SQL.MsSqlClient;conststream=yield*sql.MakeStream("SELECT * FROM Users");returnstream;});constExampleApp=Effect.all([GetGraphUsers,GetSqlUsers]).pipe(Effect.map(([s1,s2])=>Stream.merge(s1,s2)),Effect.tap(Effect.log("Starting...")),Effect.andThen(Stream.runForEach((user)=>Effect.log("user:",user))),Effect.tap(Effect.log("Finished!")),)// Create a ConfigProvider backed by Azure Key Vault// No secrets needed! Authentication happens via the credential,// so your codebase can be completely free of secrets and keys.constSecretsProvider=MakeKeyVaultProvider({vaultURL: "https://my-vault.vault.azure.us/",credential: newDefaultAzureCredential()});// Provide a fallback to process.envconstMergedProvider=SercretsProvider.pipe(ConfigProvider.orElse(ConfigProvider.fromEnv));// Convert the provider into a layer.constproviderLayer=ConfigProvider.layer(MergedProvider);// A simple provider function which constructs and then provides our layers.// We use an Effectful function for this because we need to yield* Config values.constProvideLayers=<A,E,R>(runnable: Effect.Effect<A,E,R>)=>Effect.gen(function*(){// Construct each layer...constgraphLayer=MsGraph.MsGraph.layer({clientID: yield*Config.string("example-secret-clientID"),clientSecret: yield*Config.string("example-secret-clientSecret"),tenantID: yield*Config.string("example-secret-tenantID"),scopes: ["https://graph.microsoft.com/.default"]});constsqlLayer=SQL.MsSqlClient.layer({server: yield*Config.string("example-secret-server"),password: yield*Config.string("example-secret-password"),port: yield*Config.port("server-port").pipe(Config.withDefault(1433))});// Combine all your layers!constallLayers=Layer.mergeAll(graphLayer,sqlLayer);returnyield*runnable.pipe(Effect.provide(allLayers));}).pipe(Effect.provide(providerLayer));ExampleApp.pipe(ProvideLayers,Effect.runPromise,);Contributions are welcome! Please:
- Fork the repository and create a feature branch
- Follow the existing patterns - Each module should follow the same structure and conventions
- Write tests for new functionality
- Build and validate with
bun run build&bun auditbefore submitting - Keep Effect v4 compatibility unless there's a major version bump
- Use Effect for all async operations.
- Do not create any side-effects.
- Provide JSDoc comments with usage examples
- Include type definitions (even when verbose to do so)
- Create corresponding tests
- Update the README with module description
See LICENSE file for details.
For issues, questions, or feature requests, please open an issue on GitHub at Shiv-SB/effect-services