NpgsqlRest.TsClient 1.16.0

dotnet add package NpgsqlRest.TsClient --version 1.16.0                
NuGet\Install-Package NpgsqlRest.TsClient -Version 1.16.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="NpgsqlRest.TsClient" Version="1.16.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add NpgsqlRest.TsClient --version 1.16.0                
#r "nuget: NpgsqlRest.TsClient, 1.16.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install NpgsqlRest.TsClient as a Cake Addin
#addin nuget:?package=NpgsqlRest.TsClient&version=1.16.0

// Install NpgsqlRest.TsClient as a Cake Tool
#tool nuget:?package=NpgsqlRest.TsClient&version=1.16.0                

NpgsqlRest.TsClient

Automatic Typescript Client Code Generation for NpgsqlRest

Metadata plug-in for the NpgsqlRest library.

Provides support for the generation of the [Typescript files] with interfaces and fetch functions.

The generated Typescript module can be re-generated on every build which effectively gives a static type-checking of your database.

Install

dotnet add package NpgsqlRest.TsClient --version 1.0.0

Example

Usage

app.UseNpgsqlRest(new(connectionString)
{
    EndpointCreateHandlers = [
        //
        // Configure TsClient Code Gen
        //
        new TsClient(new TsClientOptions
        {
            // Frontend file path
            FilePath = "../Frontend/src/api.ts",
            // Always overwrite
            FileOverwrite = true,
            // Include full host name in URL
            IncludeHost = true,
        }),
    ],

    // add also CRUD plugin
    SourcesCreated = sources => sources.Add(new CrudSource())
});
Database
create table customers (
  customer_id bigint not null PRIMARY KEY, 
  name text NOT NULL, 
  email text NULL, 
  created_at TIMESTAMP NOT NULL default now()
);

create function get_latest_customer() 
returns customers 
language sql 
as $$
select * 
from customers
order by created_at dec
limit 1
$$;

create function get_duplicate_email_customers() returns setof customers language sql 
as $$
select 
    customer_id, name, email, created_at
from (
    select 
        customer_id, name, email, created_at, row_number() over(partition by email) as occurance
    from customers
)
where occurance > 1
$$;

create function get_customers_count(
    _name text, 
    _email text, 
    _created_before timestamp
) 
returns bigint language sql as $$
select 
    count(*)
from customers
where 
    (_name is null or name ilike _name)
    and
    (_email is null or email ilike _email)
    and
    (_created_before is null or created_at > _created_before)
$$;

create function get_duplicate_emails() 
returns table (email text, count bigint) language sql as $$
select 
    email, count(*)
from customers
group by email
having count(*) > 1
$$;
Generated Typescript
interface ICustomersGetRequest {
    customerId?: number | null;
    name?: string | null;
    email?: string | null;
    createdAt?: Date | null;
}

interface ICustomersGetResponse {
    customerId: number | null;
    name: string | null;
    email: string | null;
    createdAt: string | null;
}

interface ICustomersPutRequest {
    customerId: number | null;
    name: string | null;
    email?: string | null;
    createdAt?: Date | null;
}

interface IGetCustomersCountRequest {
    name: string | null;
    email: string | null;
    createdBefore: Date | null;
}

interface IGetDuplicateEmailsResponse {
    email: string | null;
    count: number | null;
}

const _baseUrl = "";

const _parseQuery = (query: Record<any, any>) => "?" + Object.keys(query)
    .map(key => {
        const value = query[key] ? query[key] : "";
        if (Array.isArray(value)) {
            return value.map(s => s ? `${key}=${encodeURIComponent(s)}` : `${key}=`).join("&");
        }
        return `${key}=${encodeURIComponent(value)}`;
    })
    .join("&");

/**
* select public.customers
* 
* @remarks
* GET /api/customers
* 
* @see TABLE public.customers
*/
export async function customersGet(request: ICustomersGetRequest) : Promise<ICustomersGetResponse[]> {
    const response = await fetch(_baseUrl + "/api/customers" + _parseQuery(request), {
        method: "GET",
        headers: { "Content-Type": "application/json" },
    });
    return await response.json() as ICustomersGetResponse[];
}

/**
* update public.customers
* 
* @remarks
* POST /api/customers
* 
* @see TABLE public.customers
*/
export async function customersPost(request: ICustomersGetRequest) : Promise<void> {
    await fetch(_baseUrl + "/api/customers", {
        method: "POST",
        body: JSON.stringify(request)
    });
}

/**
* update public.customers
* returning
*     customer_id, name, email, created_at
* 
* @remarks
* POST /api/customers/returning
* 
* @see TABLE public.customers
*/
export async function customersReturningPost(request: ICustomersGetRequest) : Promise<ICustomersGetResponse[]> {
    const response = await fetch(_baseUrl + "/api/customers/returning", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(request)
    });
    return await response.json() as ICustomersGetResponse[];
}

/**
* delete from public.customers
* 
* @remarks
* DELETE /api/customers
* 
* @see TABLE public.customers
*/
export async function customersDelete(request: ICustomersGetRequest) : Promise<void> {
    await fetch(_baseUrl + "/api/customers" + _parseQuery(request), {
        method: "DELETE",
    });
}

/**
* delete from public.customers
* returning
*     customer_id, name, email, created_at
* 
* @remarks
* DELETE /api/customers/returning
* 
* @see TABLE public.customers
*/
export async function customersReturningDelete(request: ICustomersGetRequest) : Promise<ICustomersGetResponse[]> {
    const response = await fetch(_baseUrl + "/api/customers/returning" + _parseQuery(request), {
        method: "DELETE",
        headers: { "Content-Type": "application/json" },
    });
    return await response.json() as ICustomersGetResponse[];
}

/**
* insert into public.customers
* 
* @remarks
* PUT /api/customers
* 
* @see TABLE public.customers
*/
export async function customersPut(request: ICustomersPutRequest) : Promise<void> {
    await fetch(_baseUrl + "/api/customers", {
        method: "PUT",
        body: JSON.stringify(request)
    });
}

/**
* insert into public.customers
* returning
*     customer_id, name, email, created_at
* 
* @remarks
* PUT /api/customers/returning
* 
* @see TABLE public.customers
*/
export async function customersReturningPut(request: ICustomersGetRequest) : Promise<ICustomersGetResponse[]> {
    const response = await fetch(_baseUrl + "/api/customers/returning", {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(request)
    });
    return await response.json() as ICustomersGetResponse[];
}

/**
* insert into public.customers
* on conflict (customer_id) do nothing
* 
* @remarks
* PUT /api/customers/on-conflict-do-nothing
* 
* @see TABLE public.customers
*/
export async function customersOnConflictDoNothingPut(request: ICustomersPutRequest) : Promise<void> {
    await fetch(_baseUrl + "/api/customers/on-conflict-do-nothing", {
        method: "PUT",
        body: JSON.stringify(request)
    });
}

/**
* insert into public.customers
* on conflict (customer_id) do nothing
* returning
*     customer_id, name, email, created_at
* 
* @remarks
* PUT /api/customers/on-conflict-do-nothing/returning
* 
* @see TABLE public.customers
*/
export async function customersOnConflictDoNothingReturningPut(request: ICustomersPutRequest) : Promise<ICustomersGetResponse[]> {
    const response = await fetch(_baseUrl + "/api/customers/on-conflict-do-nothing/returning", {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(request)
    });
    return await response.json() as ICustomersGetResponse[];
}

/**
* insert into public.customers
* on conflict (customer_id) do update
* 
* @remarks
* PUT /api/customers/on-conflict-do-update
* 
* @see TABLE public.customers
*/
export async function customersOnConflictDoUpdatePut(request: ICustomersPutRequest) : Promise<void> {
    await fetch(_baseUrl + "/api/customers/on-conflict-do-update", {
        method: "PUT",
        body: JSON.stringify(request)
    });
}

/**
* insert into public.customers
* on conflict (customer_id) do update
* returning
*     customer_id, name, email, created_at
* 
* @remarks
* PUT /api/customers/on-conflict-do-update/returning
* 
* @see TABLE public.customers
*/
export async function customersOnConflictDoUpdateReturningPut(request: ICustomersPutRequest) : Promise<ICustomersGetResponse[]> {
    const response = await fetch(_baseUrl + "/api/customers/on-conflict-do-update/returning", {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(request)
    });
    return await response.json() as ICustomersGetResponse[];
}

/**
* function public.get_customers_count(
*     _name text,
*     _email text,
*     _created_before timestamp without time zone
* )
* returns bigint
* 
* @remarks
* GET /api/get-customers-count
* 
* @see FUNCTION public.get_customers_count
*/
export async function getCustomersCount(request: IGetCustomersCountRequest) : Promise<number> {
    const response = await fetch(_baseUrl + "/api/get-customers-count" + _parseQuery(request), {
        method: "GET",
    });
    return Number(await response.text());
}

/**
* function public.get_duplicate_email_customers()
* returns table(
*     customer_id bigint,
*     name text,
*     email text,
*     created_at timestamp without time zone
* )
* 
* @remarks
* GET /api/get-duplicate-email-customers
* 
* @see FUNCTION public.get_duplicate_email_customers
*/
export async function getDuplicateEmailCustomers() : Promise<ICustomersGetResponse[]> {
    const response = await fetch(_baseUrl + "/api/get-duplicate-email-customers", {
        method: "GET",
        headers: { "Content-Type": "application/json" },
    });
    return await response.json() as ICustomersGetResponse[];
}

/**
* function public.get_duplicate_emails()
* returns table(
*     email text,
*     count bigint
* )
* 
* @remarks
* GET /api/get-duplicate-emails
* 
* @see FUNCTION public.get_duplicate_emails
*/
export async function getDuplicateEmails() : Promise<IGetDuplicateEmailsResponse[]> {
    const response = await fetch(_baseUrl + "/api/get-duplicate-emails", {
        method: "GET",
        headers: { "Content-Type": "application/json" },
    });
    return await response.json() as IGetDuplicateEmailsResponse[];
}

/**
* function public.get_latest_customer()
* returns customers
* 
* @remarks
* GET /api/get-latest-customer
* 
* @see FUNCTION public.get_latest_customer
*/
export async function getLatestCustomer() : Promise<ICustomersGetResponse> {
    const response = await fetch(_baseUrl + "/api/get-latest-customer", {
        method: "GET",
        headers: { "Content-Type": "application/json" },
    });
    return await response.json() as ICustomersGetResponse;
}

Options

See the TsClientOptions.cs source file.

namespace NpgsqlRest.TsClient;

public class TsClientOptions(
    string filePath = default!,
    bool fileOverwrite = false,
    bool includeHost = false,
    string? customHost = null,
    CommentHeader commentHeader = CommentHeader.Simple,
    bool commentHeaderIncludeComments = true,
    bool includeStatusCode = false,
    bool bySchema = false,
    bool createSeparateTypeFile = true,
    string? importBaseUrlFrom = null,
    string? importParseQueryFrom = null,
    bool includeParseUrlParam = false,
    bool includeParseRequestParam = false,
    string[]? skipRoutineNames = null,
    string[]? skipFunctionNames = null,
    string[]? skipPaths = null,
    string defaultJsonType = "string",
    bool useRoutineNameInsteadOfEndpoint = false,
    bool exportUrls = false,
    bool skipTypes = false)
{
    /// <summary>
    /// File path for the generated code. Set to null to skip the code generation. Use {0} to set schema name when BySchema is true
    /// </summary>
    public string? FilePath { get; set; } = filePath;

    /// <summary>
    /// Force file overwrite.
    /// </summary>
    public bool FileOverwrite { get; set; } = fileOverwrite;

    /// <summary>
    /// Include current host information in the URL prefix.
    /// </summary>
    public bool IncludeHost { get; set; } = includeHost;

    /// <summary>
    /// Set the custom host prefix information.
    /// </summary>
    public string? CustomHost { get; set; } = customHost;

    /// <summary>
    /// Adds comment header to above request based on PostgreSQL routine
    /// Set None to skip.
    /// Set Simple (default) to add name, parameters and return values to comment header.
    /// Set Full to add the entire routine code as comment header.
    /// </summary>
    public CommentHeader CommentHeader { get; set; } = commentHeader;

    /// <summary>
    /// When CommentHeader is set to Simple or Full, set to true to include routine comments in comment header.
    /// </summary>
    public bool CommentHeaderIncludeComments { get; set; } = commentHeaderIncludeComments;

    /// <summary>
    /// Set to true to include status code in response: {status: response.status, response: model}
    /// </summary>
    public bool IncludeStatusCode { get; set; } = includeStatusCode;

    /// <summary>
    /// Create files by PostgreSQL schema. File name will use formatted FilePath where {0} is is the schema name in the pascal case.
    /// </summary>
    public bool BySchema { get; set; } = bySchema;

    /// <summary>
    /// Create separate file with global types {name}Types.d.ts
    /// </summary>
    public bool CreateSeparateTypeFile { get; set; } = createSeparateTypeFile;

    /// <summary>
    /// Lines to add to each header. {0} format placeholder is current timestamp
    /// </summary>
    public List<string> HeaderLines { get; set; } = ["// autogenerated at {0}", "", ""];

    /// <summary>
    /// Module name to import "baseUrl" constant, instead of defining it in a module.
    /// </summary>
    public string? ImportBaseUrlFrom { get; set; } = importBaseUrlFrom;

    /// <summary>
    /// Module name to import "pasreQuery" function, instead of defining it in a module.
    /// </summary>
    public string? ImportParseQueryFrom { get; set; } = importParseQueryFrom;

    /// <summary>
    /// Include optional parameter `parseUrl: (url: string) => string = url=>url` that will parse constructed url.
    /// </summary>
    public bool IncludeParseUrlParam { get; set; } = includeParseUrlParam;

    /// <summary>
    /// Include optional parameter `parseRequest: (request: RequestInit) => RequestInit = request=>request` that will parse constructed request.
    /// </summary>
    public bool IncludeParseRequestParam { get; set; } = includeParseRequestParam;

    /// <summary>
    /// Array of routine names to skip (without schema)
    /// </summary>
    public string[] SkipRoutineNames { get; set; } = skipRoutineNames ?? [];

    /// <summary>
    /// Array of generated function names to skip (without schema)
    /// </summary>
    public string[] SkipFunctionNames { get; set; } = skipFunctionNames ?? [];

    /// <summary>
    /// Array of url paths to skip
    /// </summary>
    public string[] SkipPaths { get; set; } = skipPaths ?? [];

    /// <summary>
    /// Array of schema names to skip
    /// </summary>
    public string[] SkipSchemas { get; set; } = skipPaths ?? [];

    /// <summary>
    /// Default TypeScript type for JSON types.
    /// </summary>
    public string DefaultJsonType { get; set; } = defaultJsonType;

    /// <summary>
    /// Use routine name instead of endpoint name when generating a function name.
    /// </summary>
    public bool UseRoutineNameInsteadOfEndpoint { get; set; } = useRoutineNameInsteadOfEndpoint;

    /// <summary>
    /// Export URLs as constants.
    /// </summary>
    public bool ExportUrls { get; set; } = exportUrls;

    /// <summary>
    /// Skip generating types and produce pure JavaScript code. Setting this to true will also change .ts extension to .js where applicable.
    /// </summary>
    public bool SkipTypes { get; set; } = skipTypes;
}

Library Dependencies

  • NpgsqlRest 2.5.0

Contributing

Contributions from the community are welcomed. Please make a pull request with a description if you wish to contribute.

License

This project is licensed under the terms of the MIT license.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.16.0 77 12/21/2024
1.15.0 92 11/23/2024
1.14.0 78 11/17/2024
1.13.0 79 10/30/2024
1.10.0 115 9/3/2024
1.9.1 82 8/2/2024
1.9.0 100 7/13/2024
1.8.1 97 6/11/2024
1.8.0 108 6/9/2024
1.7.0 76 5/2/2024
1.6.0 121 4/19/2024
1.5.0 112 4/15/2024
1.4.3 125 4/14/2024
1.4.2 113 4/10/2024
1.4.1 103 4/10/2024
1.4.0 110 4/10/2024
1.3.1 129 4/8/2024
1.3.0 113 4/8/2024
1.2.0 119 4/4/2024
1.1.0 116 3/29/2024
1.0.0 118 3/26/2024