GithubHelp home page GithubHelp logo

vishalbalaji / trpc-svelte-query-adapter Goto Github PK

View Code? Open in Web Editor NEW
67.0 67.0 6.0 234 KB

A simple adapter to use `@tanstack/svelte-query` with trpc, similar to `@trpc/react-query`.

TypeScript 96.77% JavaScript 3.23%
adapter react-query svelte svelte-query sveltekit tanstack-query trpc typescript

trpc-svelte-query-adapter's People

Contributors

icflorescu avatar smaven avatar vishalbalaji avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

trpc-svelte-query-adapter's Issues

Allow for naming of queries with `createServerQueries`

Thanks for this library first of all, it's great! ๐Ÿ˜ƒ

Instead of doing this:

export const load: PageLoad = async (event) => {
  const { queryClient } = await event.parent();
  const api = trpc(event, queryClient);
  return {
    queries: await api.createServerQueries((t) => [
      t.public.todos.list(),
      t.public.hello.get()
    ])
  };
};

I'd like to be able to name the queries like so:

export const load: PageLoad = async (event) => {
  const { queryClient } = await event.parent();
  const api = trpc(event, queryClient);
  return {
    queries: await api.createServerQueries((t) => [
      {
        listTodos: t.public.todos.list(),
        getHello: t.public.hello.get()
      }
    ])
  };
};

Right now i'm doing something like this on the frontend

<script lang="ts">
  import { page } from '$app/stores';
  import { trpc } from '$lib/trpc/client';
  import type { PageData } from './$types';

  export let data: PageData;

  const queries = data.queries();

  $: listTodos = $queries[0];
  $: getHello = $queries[1];

  const api = trpc($page);
  const addTodo = api.public.todos.add.createMutation();
  const deleteTodo = api.public.todos.delete.createMutation();
</script>

I'd love to be able to access the queries directly like so:

$queries.listTodos

and

$queries.getHello

Passing inputs to createServerQuery on the client-side

Let's say I'm using createServerQuery to prefetch a list of users on the server and pass the query to the client:

// +page.ts
import type { PageLoad } from './$types';
import { trpc } from '$lib/trpc/client';

export const load: PageLoad = async (event) => {
  const { queryClient } = await event.parent();
  const client = trpc(event, queryClient);

  return {
    users: await client.user.all.createServerQuery(),
  };
};

I can use the query result to display content accordingly:

<!-- +page.svelte -->
<script lang="ts">
  import type { PageData } from './$types';

  export let data: PageData;

  const users = data.users();
</script>

<h1>List of users</h1>

{#if $users.isFetching}
  <p>Loading...</p>
{:else if $users.error}
  <p>Error: {$users.error.message}</p>
{:else if $users.isSuccess}
  <ul>
    {#each $users.data as user}
      <li>{user.name}</li>
    {/each}
  </ul>
{:else}
  <p>No users found</p>
{/if}

Now I want to filter the list of users as someone types in an input field. This requires me to send an input to the query. My guess was I would be able to pass the input to data.users() just like I can pass the input to trpc($page).user.all.createQuery(input) but this does not work and I could not find an example to achieve this.

<!-- +page.svelte -->
<script lang="ts">
  import type { PageData } from './$types';

  export let data: PageData;

  let filter = '';

  $: users = data.users({ name: filter });
</script>

<input type="text" bind:value={filter} placeholder="Filter users" />

...

I might be missing something really obvious here as this seems to me like a very common use case: fetching data on the server, passing it to the client so that the client doesn't have to load it again when it hydrates and then the subsequent loading of data happens on the client when inputs vary depending on user actions.

Apologies if this has been already answered somewhere, I dug into the README and issues but couldn't find anything.

Support for cancelling queries?

Firstly, thanks so much for making this adapter.

I'm trying to cancel a query but I'm not observing any effect from my attempts. Reading the trpc docs for the 'useQuery' function, they mention needing to set 'abortOnUnmount' to true, but I can't find a way to set this using this adapter. Am I missing something?

Anyone have a working example they can share? ๐Ÿ™

Brilliant package!

This deserves way more love.

I removed os much boilerplate and repetitiveness using this ๐Ÿš€

How to run queries outside of component initialization?

In trpc-sveltekit, the following is possible (from https://icflorescu.github.io/trpc-sveltekit/getting-started):

<script lang="ts">
  import { page } from '$app/stores';
  import { trpc } from '$lib/trpc/client';

  let greeting = 'press the button to load data';
  let loading = false;

  const loadData = async () => {
    loading = true;
    greeting = await trpc($page).greeting.query(); // (substituted with `createQuery()`)
    loading = false;
  };
</script>

<h6>Loading data in<br /><code>+page.svelte</code></h6>

<a
  href="#load"
  role="button"
  class="secondary"
  aria-busy={loading}
  on:click|preventDefault={loadData}>Load</a
>
<p>{greeting}</p>

Attempting to implement this pattern with trpc-svelte-query-adapter results in an error of Function called outside component initialization. It seems due to the fact that svelte-query's createQuery method is called (by trpc-svelte-query-adapter) without it being passed a queryClient, so it tries to get it from the current component context, which of course doesn't exist outside of component initialization.

Is there a different way to implement this pattern, or would modifying these lines to pass in a queryClient be better?

Infinite queries?

Hello, is it possible to to this pattern with this package? It seems like all input to trpcWithQuery goes to the endpoint and not to setup the query as seen here.
Screenshot 2023-03-09 at 7 57 47 PM

[Q/A] I realized I do not have to use the query fn returned from layout/page.ts for ssr?

Thank you for the awesome lib :)

So in your ssr example, you returned the query function from page.ts

// page.ts->load()
return {
    foo: await client.greeting.createServerQuery('foo')
}

And then used used the returned function instead of greeting.createQuery

  export let data: PageData;

  const foo = data.foo();

I realized once i called createServerQuery inside layout load, my createQuery was doing ssr fine. So is there a reason you did this the way you did it

client.greeting.createQuery('foo')

One more question, my queries seem to refetch on mount regardless of what i set to createServerQuery or createQuery, is this related to this adapter? Can you point me to where should i look to resolve this?

refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
staleTime: Infinity,
enabled: false,

trpc client error

I get Unexpected token '<', "<!DOCTYPE "... is not valid JSON when running the example.
i figure its the httpBatchLink that is the culprit here, but i can't figure out what this url is supposed to point to.
I read the tRPC docs and cannot seem to wrap my mind around this.
I thought maybe you could help

createUtils getData not returning data

I think getQueryKey is returning the wrong key?

setup

<script lang="ts">
import { page } from '$app/stores';
import { useQueryClient } from '@tanstack/svelte-query'	
import { trpc } from '$lib/trpc/client';
import { writable } from "svelte/store";
import clonedeep  from "lodash.clonedeep";

const queryClient = useQueryClient();
const utils = trpc($page, queryClient).createUtils();
let id = 5;

const greeting = trpc($page, queryClient).greeting.createQuery({
		where: {
			id: id
		}
});

$: {
	if($greeting?.status === 'success'){
		const queriesData = queryClient.getQueriesData();
		console.log("queriesData",queriesData);
		const queryKey = trpc($page, queryClient).greeting.getQueryKey({
			where: {
				id: id
			}
		});
		console.log("queryKey", queryKey);
		const data = utils.greeting.getData();
		console.log("data",data);


		console.log("queryKey from utils", queryKey)
		//no data
		console.log('no data :(', queryClient.getQueryData(queryKey));

		const newQueryKey = clonedeep(queryKey);
		console.log("newQueryKey", newQueryKey)
		//data
		newQueryKey[1]['type'] = 'query';
		console.log('data found :D', queryClient.getQueryData(newQueryKey))
	}
}

	
</script>

{#if $greeting.isLoading}
	loading...
{:else}
	{$greeting.data}
{/if}
<h6>Loading data in<br /><code>+page.svelte</code></h6>

image
image

Am I doing something wrong? I have made stackblitz
https://stackblitz.com/edit/sveltejs-kit-template-default-thhat3?description=The%20default%20SvelteKit%20template,%20generated%20with%20create-svelte&file=src%2Froutes%2F%2Blayout.svelte,src%2Froutes%2F%2Bpage.svelte,src%2Flib%2Ftrpc%2Frouter.ts,src%2Flib%2Ftrpc%2Fcontext.ts,src%2Fhooks.server.ts,src%2Flib%2Ftrpc%2Fclient.ts,src%2Fapp.html&title=SvelteKit%20Default%20Template

CreateQuery in +layout.svelte

When I am accessing CreateQuery in +layout.svelte it is showing an error of No QueryClient in svelte context. In addition I am not able to access the CreateServerQuery result in +layout.svelte. I am using trpc-sveltekit by @icflorescu. Here is the code

// lib/trpc/trpc.ts

import { svelteQueryWrapper } from '$lib/trpc-svelte-query';
import type { QueryClient } from '@tanstack/svelte-query';
import type { Router } from '$lib/trpc/router';
import  {createTRPCClient, type TRPCClientInit } from 'trpc-sveltekit';

let browserClient: ReturnType<typeof svelteQueryWrapper<Router>>;
export function trpc(init?: TRPCClientInit, queryClient?: QueryClient) {
  const isBrowser = typeof window !== 'undefined';
  if (isBrowser && browserClient) return browserClient;
  const client = svelteQueryWrapper<Router>({
    client: createTRPCClient<Router>({ init }),
    queryClient
  });
  if (isBrowser) browserClient = client;
  return client;
}
// +layout.ts
import { QueryClient } from "@tanstack/svelte-query";
import type { LayoutLoad } from "./$types";
import type { LoadEvent } from "@sveltejs/kit";
import { trpc } from "$lib/trpc/client";

export const load: LayoutLoad = async (event: LoadEvent) => {
    const queryClient = new QueryClient()
    const client = trpc(event, queryClient)
    return {queryClient, grettingData: client.greeting.createServerQuery()}
}
// +layout.svelte
<script lang="ts">
	import type { LayoutData } from "./$types";
    import {QueryClientProvider, useQueryClient} from "@tanstack/svelte-query"
    export let data: LayoutData;
    
    const greetingData = data.grettingData()
    
    console.log($greetingData.data)
</script>

<QueryClientProvider client={data.queryClient}>
    
    <slot />
    
</QueryClientProvider>

Incorrect opts types on many functions

When using .createQuery, .createInfiniteQuery, and every other function that takes opts?:, the queryKey is always required

For example this has no type errors:

const search = trpc($page).search.text.createQuery({ text: input.trim() })

But this has an error:

// Property 'queryKey' is missing in type '{ enabled: boolean; }' but required in type ...
const search = trpc($page).search.text.createQuery({ text: input.trim() }, { enabled: input.trim().length > 0 })

In the dist, queryKey is always overridden so there is no reason why queryKey should even be available

image

My suggestion is adding Partial<> to each of the opts?: however you know more than me if this is a good idea
image
image

How to stream queries to Pages/Components?

Instead of doing the following (to prerender queries on the server):

// +page.ts
import { trpc } from '$lib/trpc/client';
import type { PageLoad } from './$types';

export const load: PageLoad = async (event) => {
  const { queryClient } = await event.parent();
  const api = trpc(event, queryClient);
  return {
    queries: await api.createServerQueries((t) => [
      t.authed.todos.all(),
      t.public.hello.get()
    ])
  };
};

I'd like to be able to stream the result of queries for example on my page. I'm new to Svelte, but I know that Svelte does support streaming data via promises. Apparently if you don't await the data in the server load function, you can use the await block on the client to show a loading fallback while the promise resolves.

For example (From: https://kit.svelte.dev/docs/load#streaming-with-promises):

// +page.svelte.ts
{#await data.streamed.comments}
  Loading...
{:then value}
  {value}
{/await}

In relation to createServerQueries, is there a way I can do streaming with it, so I can have faster page loads? Any advice/guidance would be super appreciated!

Incorrect data types for createInfiniteQuery

createInfiniteQuery has the incorrect type for data

For example

// route.ts
router({
  infinite: protectedProcedure
    .use(logger)
    .input(
      z.object({
        cursor: z.number(),
      })
    )
    .query(async ({ input, ctx }) => {
      console.log(input)
      const { cursor } = input
      const posts = await db.query.clubPosts.findMany({
        limit: limit + 1,
        offset: cursor,
        ...
        orderBy: (post, { desc }) => [desc(post.createdAt)],
      })

      let nextCursor: number | null = null
      if (posts.length > limit) {
        posts.pop()
        nextCursor = cursor + limit
      }
      return { posts, nextCursor }
    }),
})
// page.svelte
  const feed = trpc($page).explore.infinite.createInfiniteQuery(
    {},
    {
      initialPageParam: 0,
      getNextPageParam: ({ nextCursor }) => nextCursor,
    }
  )

$feed.data incorrectly shows the raw query return type, rather than pageParams and pages
image
image

This can be fixed by wrapping InfiniteData on TData in the d.ts
image

Loading trpc in +page.server

heya thanks for the lib.

Am I right thinking that if I want to use trpc on a server code, I need an entirely different isntance?

eg.

// $lib/trpc.ts

import type { AppRouter } from '$lib/server/trpc/routes';
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
import SuperJSON from 'superjson';
import { svelteQueryWrapper } from 'trpc-svelte-query-adapter';
import type { QueryClient } from '@tanstack/svelte-query';

const client = createTRPCProxyClient<AppRouter>({
	links: [
		httpBatchLink({
			url: 'http://localhost:5173/api/trpc'
		})
	],
	transformer: SuperJSON
});

export const trpc = (queryClient?: QueryClient) =>
	svelteQueryWrapper<AppRouter>({
		client,
		queryClient
	});

export const trpcOnServer = (fetch) =>
	createTRPCProxyClient<AppRouter>({
		links: [
			httpBatchLink({
				url: '/api/trpc',
				fetch
			})
		],
		transformer: SuperJSON
	});
// +page.server.ts


import type { Actions } from './$types';
import { trpcOnServer } from '$lib/trpc';

export const actions: Actions = {
	default: async ({ request, fetch }) => {
		const data = await request.formData();
		const email = data.get('email');

		const api = trpcOnServer(fetch);

		const [newProject] = await api.projects.save.mutate({
		        email
		});
	}
};

Type error when using getQueryKey

I have the following procedure:

export const userRouter = {
  all: publicProcedure
    .input(
      z
        .object({
          name: z.string(),
        })
        .optional(),
    )
    .query(async ({ input }) => {
      return 'something'
    }),
} satisfies TRPCRouterRecord;

Trying to get the query key gives me the following type error:
image

Seems like the type GetQueryKey is not accounting for the void type

type GetQueryKey<TInput = undefined> = TInput extends undefined
	? {
		[ProcedureNames.queryKey]: () => QueryKey
	}
	: {
		/**
		 * Method to extract the query key for a procedure
		 * @param type - defaults to `any`
		 */
		[ProcedureNames.queryKey]: (input: TInput, type?: QueryType) => QueryKey
	} & {}

This can be fixed by modifying the type as:

type GetQueryKey<TInput = undefined> = [TInput] extends [undefined] | [void]
	? {
		[ProcedureNames.queryKey]: () => QueryKey
	}
	: {
		/**
		 * Method to extract the query key for a procedure
		 * @param type - defaults to `any`
		 */
		[ProcedureNames.queryKey]: (input: TInput, type?: QueryType) => QueryKey
	} & {}

If it looks good, I'll be happy to create a PR for this fix.

P.S. Great work on this package! ๐Ÿ™Œ

What needs to be done for MVP?

The list of features that need to be implemented from @trpc/react-query according to trpc docs and personal testing are as follows:

  • useQuery
  • useMutation
  • useInfiniteQuery
  • useSubscription
  • getQueryKey
  • useContext
    • fetch
    • prefetch
    • fetchInfinite
    • prefetchInfinite
    • invalidate
    • refetch
    • reset
    • cancel
    • setData
    • getData
    • setInfiniteData
    • getInfiniteData
  • useQueries

`@typescript-eslint/eslint-plugin` and `@typescript-eslint/parser` peer dependency blocking upgrades in project

Is this peer dependency necessary? (I'm genuinely not sure).

It's blocking linting upgrades in my project without applying --force to the install.

Example:

npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR! 
npm ERR! While resolving: [email protected]
npm ERR! Found: @typescript-eslint/[email protected]
npm ERR! node_modules/@typescript-eslint/eslint-plugin
npm ERR!   dev @typescript-eslint/eslint-plugin@"^7.1.1" from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer @typescript-eslint/eslint-plugin@"^6.10.0" from [email protected]
npm ERR! node_modules/trpc-svelte-query-adapter
npm ERR!   trpc-svelte-query-adapter@"^2.2.5" from the root project
npm ERR! 
npm ERR! Conflicting peer dependency: @typescript-eslint/[email protected]
npm ERR! node_modules/@typescript-eslint/eslint-plugin
npm ERR!   peer @typescript-eslint/eslint-plugin@"^6.10.0" from [email protected]
npm ERR!   node_modules/trpc-svelte-query-adapter
npm ERR!     trpc-svelte-query-adapter@"^2.2.5" from the root project
npm ERR! 
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.

Library isn't compatible with @tanstack/svelte-query v5

I was having error while trying to use this library with @tanstack/svelte-query v5.

This library imports some stuff that aren't available in the @tanstack/svelte-query v5. Specifically:ResetQueryFilters and CreateQueriesResult

createServerQuery not respecting staletime

I have the following code in a /+page.ts

export const load = (async (evt) => {
  const { queryClient } = await evt.parent();
  const client = trpcQuery(evt, queryClient);
  return {
    data: client.test.list.createServerQuery({}, { staleTime: 10 * 60 }),
  };
}) satisfies PageLoad;

Every time I navigate to the page the query is refetched and the staleTime is not used.

I understand that If I refresh the browser the queryClient is cleared out, but I was expecting the stale time to respected for client-side navigation.

Is there a setting where I can enable respecting staleTime for client-side navigation?

Variables/Data are not send on mutations

Seems like the variables are not sent while using useMutation(), I am not sure if those are send on useQuery() or not but for useMutation(), no data is sent back to server

Instructions to setup with api url

In case anybody is interested in using this with the createTRPCProxyClient object on SvelteKit to expose an /api/trpc/ route... This is how I've done it

// $lib/trpc.ts
import type { AppRouter } from '$lib/server/trpc/routes';
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
import SuperJSON from 'superjson';
import { svelteQueryWrapper } from 'trpc-svelte-query-adapter';
import type { QueryClient } from '@tanstack/svelte-query';

const client = createTRPCProxyClient<AppRouter>({
	links: [
		httpBatchLink({
			url: 'http://localhost:5173/api/trpc'
		})
	],
	transformer: SuperJSON
});

export const trpc = (queryClient?: QueryClient) =>
	svelteQueryWrapper<AppRouter>({
		client,
		queryClient
	});
// $lib/server/trpc/index.tsimport { initTRPC } from '@trpc/server';
import type { Context } from '$lib/server/trpc/context';
import superjson from 'superjson';
import { ZodError } from 'zod';

const t = initTRPC.context<Context>().create({
	transformer: superjson
});
export const { procedure, router, middleware, createCallerFactory } = t;
// $lib/server/trpc/context.ts
import type { RequestEvent } from '@sveltejs/kit';
import type { inferAsyncReturnType } from '@trpc/server';

export const createContext = async (event: RequestEvent) => ({ event });
export type Context = inferAsyncReturnType<typeof createContext>;
// $lib/server/trpc/caller.ts
import { createCallerFactory } from '$lib/server/trpc';
import { appRouter } from '$lib/server/trpc/routes';

export const createCaller = createCallerFactory(appRouter);
// $lib/server/trpc/routes/index.ts
import { clients } from './clients';
import { files } from './files';
import { projects } from './projects';
import { users } from './users';
import { router } from '$lib/server/trpc';

export const appRouter = router({
	users
});

export type AppRouter = typeof appRouter;
// $lib/server/trpc/routes/users.ts
import { isLogged } from '$lib/server/trpc/middlewares/auth';
import { router, procedure } from '$lib/server/trpc';
import { db } from '$lib/server/database/index';
import { client } from '$lib/server/database/schema';
import { eq } from 'drizzle-orm';
import { clientSchema } from '$lib/zod';

export const users = router({
	list: procedure
		// use query for GET
		.query(async () => db.select().from(client)),
	delete: procedure
		.use(isLogged)
		.input(userSchema.pick({ id: true }))
		// use mutation for POST
		.mutation(async ({ input: { id } }) => {
			db.delete(user).where(eq(user.id, id));
		})
});
// routes/api/trpc/[...path]/+server.ts
import { createContext } from '$lib/server/trpc/context';
import { appRouter } from '$lib/server/trpc/routes';
import type { RequestHandler } from '@sveltejs/kit';
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';

const requestHandler: RequestHandler = (event) =>
	fetchRequestHandler({
		req: event.request,
		router: appRouter,
		endpoint: '/api/trpc',
		createContext: () => createContext(event)
	});

export const GET = requestHandler;
export const POST = requestHandler;
// routes/+layout.ts
import { browser } from '$app/environment';
import { QueryClient } from '@tanstack/svelte-query';
import type { LayoutLoad } from './$types';

export const load: LayoutLoad = async () => {
	const queryClient = new QueryClient({
		defaultOptions: {
			queries: {
				enabled: browser,
				staleTime: 60 * 1000
			}
		}
	});

	return { queryClient };
};
// routes/+layout.svelte
<script lang="ts">
	import { QueryClientProvider } from '@tanstack/svelte-query';
	import type { PageData } from './$types';

	export let data: PageData;
</script>

<QueryClientProvider client={data.queryClient}>
	<main>
		<slot />
	</main>
</QueryClientProvider>

I am using Lucia so that's why I have the ctx.event.locals.user and session in the auth middleware to stop non-logged in users acceessing certain routes.

// $lib/server/trpc/middlewares/auth.ts
import { middleware } from '$lib/server/trpc';
import { TRPCError } from '@trpc/server';

export const isLogged = middleware(async ({ next, ctx }) => {
	if (!ctx.event.locals.user && !ctx.event.locals.session)
		throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Not authorized!' });
	return next();
});

Bonus in case you're interested to see what's in the Zod user schema.

// $lib/zod.ts
import { z } from 'zod';
import validator from 'validator';

export const roles = ['admin', 'user'] as const;
export const roleEnumSchema = z.enum(roles);
export const userSchema = z.object({
	id: z.string()
	role: roleEnumSchema,
	username: z
		.string()
		.min(3)
		.max(31)
		.refine((val) => validator.isAlphanumeric(val), {
			message: 'Username can only contain letters and numbers.'
		}),
	password: z.string().min(3).max(31)
});
export type User = z.infer<typeof userSchema>;

The `trpc: { abortOnUnmount: boolean }` type is now (unintentionally?) required on every query

This happens due to TRPCQueryOpts now being required on createQuery:

[ProcedureNames.query]: <TData = TOutput>(input: TInput, opts?: CreateQueryOptions<TOutput, TError, TData> & TRPCQueryOpts)

Based on my cursor read, this isn't required since all lookups are of opts?.trpc?.abortOnUnmount - implying that null values are valid here. Should the type be changed?

An error in documentation

Hey. Nice package.
I noticed an error in your documentation,.

export function trpc(queryClient?: QueryClient) {
  svelteQueryWrapper<Router>({
    client,
    queryClient
  });
};

You need to return the svelteQueryWrapper

export function trpc(queryClient?: QueryClient) {
 return svelteQueryWrapper<Router>({
    client,
    queryClient
  });
};

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.