Code Monkey home page Code Monkey logo

reactive-jikan's Introduction

Reactive Jikan

Maven Central GitHub Workflow Codecov

A fast and fully-typed API wrapper for the Jikan API V4, with the power of Project Reactor and reactive streams.

Supported versions

API version reactive-jikan version
V3 1.1.0
V4 2.2.0

Installation

Add the following dependency to your build file.

If using Gradle (build.gradle):

dependencies {
    implementation 'net.sandrohc:reactive-jikan:2.2.0'
}

If using Maven (pom.xml):

<dependency>
    <groupId>net.sandrohc</groupId>
    <artifactId>reactive-jikan</artifactId>
    <version>2.2.0</version>
</dependency>

If you want to use development versions, see: https://jitpack.io/#net.sandrohc/reactive-jikan

Usage

The Reactive Jikan library provides all queries through the Query classes. Once the desired query is created (the QueryFactory can be used to help you navigate through all the available queries), it can be passed to the Jikan instance. Some queries have optional parameters, like the search queries; for more details, please see the javadocs for each query or refer to the official Jikan API documentation.

After the query has been configured with the desired parameters, it can be executed by calling query.execute() (or alternatively jikan.query(yourQuery)). The value returned is a reactive stream of type Mono<T> or Flux<T> depending on the query. The HTTP request is only made once the methods subscribe() or block() are called. For more information on reactive queries, please read the relevant documentation.

Here are some examples on how to use this library:

// Create the Jikan instance with default parameters.
// You can also use the builder, JikanBuilder, to specify custom parameters.
Jikan jikan = new Jikan(); 

// Fetch the anime with ID 1. Returns a single value, a mono.
Anime anime = jikan.query().anime().get(1)
        .execute()
        .block();

// Search for 'sword art online'. Returns a list of values, a flux.
Collection<Anime> results = jikan.query().anime().search()
		.query("sword art online")
		.limit(5)
		.status(AnimeStatus.AIRING)
		.orderBy(AnimeOrderBy.MEMBERS, SortOrder.ASCENDING)
		.execute()
		.collectList()
		.block();

Android

The following changes are necessary to use this library on Android.

Jikan jikan = new Jikan.JikanBuilder()
        .httpClientCustomizer(httpClient -> httpClient.resolver(DefaultAddressResolverGroup.INSTANCE))
        .build();

Caching

Caching comes disabled by default. To enable it, please provide an implementation of JikanCache to the Jikan instance.

The following is an example using Caffeine as the cache library. Beware that Caffeine is not supported in Android!

// build.gradle
dependencies {
	implementation 'com.github.ben-manes.caffeine:caffeine:2.9.3'
}

// App.java
JikanCache jikanCache = new JikanCache() {
    protected final Cache<String, JikanValueHolder> cache = Caffeine.newBuilder().maximumSize(10_000).expireAfter(new JikanExpiry()).build();

    public void put(String key, Object value, OffsetDateTime expires) {
        cache.put(key, new JikanValueHolder(value, expires));
    }

    public Optional<Object> get(String key) {
        return Optional.ofNullable(cache.getIfPresent(key)).map(holder -> holder.value);
    }

    class JikanValueHolder {
        public final Object value;
        public final long expireTime;

        public JikanValueHolder(Object value, OffsetDateTime expires) {
            this.value = value;
            this.expireTime = TimeUnit.SECONDS.toNanos(expires.toEpochSecond());
        }
    }

    class JikanExpiry implements Expiry<String, JikanValueHolder> {
        public long expireAfterCreate(String key, JikanValueHolder value, long currentTime) {
            return value.expireTime;
        }

        public long expireAfterUpdate(String key, JikanValueHolder value, long currentTime, long currentDuration) {
            return currentDuration; // Do not modify expire date
        }

        public long expireAfterRead(String key, JikanValueHolder value, long currentTime, long currentDuration) {
            return currentDuration; // Do not modify expire date
        }
    }
};

Jikan jikan = new Jikan.JikanBuilder()
        .cache(jikanCache)
        .build();

Endpoints

The following is an exhaustive list of all the endpoints and query classes supported by the Reactive Jikan library.

Endpoint Query Result
Anime
/{id} AnimeQuery Anime
/{id}/characters_staff AnimeCharactersAndStaffQuery AnimeCharactersAndStaff
/{id}/episodes/{page} AnimeEpisodesQuery AnimeEpisodes
/{id}/news AnimeNewsQuery AnimeNews
/{id}/pictures AnimePicturesQuery (list) AnimePicture
/{id}/videos AnimeVideosQuery AnimeVideos
/{id}/statistics AnimeStatisticsQuery Stats
/{id}/forum AnimeForumQuery (list) ForumTopic
/{id}/moreinfo AnimeMoreInfoQuery MoreInfo
/{id}/reviews/{page} AnimeReviewsQuery (list) Review
/{id}/recommendations AnimeRecommendationsQuery (list) Recommendation
/{id}/userupdates/{page} AnimeUserUpdatesQuery (list) UserUpdate
Manga
/{id} MangaQuery Manga
/{id}/characters MangaCharactersQuery (list) RoleSubEntity
/{id}/news MangaNewsQuery (list) NewsArticle
/{id}/pictures MangaPicturesQuery (list) Pictures
/{id}/statistics MangaStatisticsQuery Stats
/{id}/forum MangaForumQuery (list) ForumTopic
/{id}/moreinfo MangaMoreInfoQuery MoreInfo
/{id}/reviews/{page} MangaReviewsQuery (list) Review
/{id}/recommendations MangaRecommendationsQuery Recommendations
/{id}/userupdates/{page} MangaUserUpdatesQuery (list) UserUpdate
Person
/{id} PersonQuery Person
/{id}/pictures PersonPicturesQuery (list) Picture
Character
/{id} CharacterQuery Character
/{id}/pictures CharacterPicturesQuery (list) Picture
Search
/anime AnimeSearchQuery (list) AnimeSearchSub
/manga MangaSearchQuery (list) MangaSearchSub
/person PersonSearchQuery (list) PersonSearchSub
/character CharacterSearchQuery (list) CharacterSearchSub
Season
/{year}/{season} SeasonQuery (list) SeasonAnime
/archive SeasonArchiveQuery (list) SeasonArchiveYear
/later SeasonLaterQuery (list) SeasonAnime
Schedule
/{day} ScheduleQuery Schedule
Top
/anime/{page}/{subtype} AnimeTopQuery (list) AnimeTopSub
/manga/{page}/{subtype} MangaTopQuery (list) MangaTopSub
Genre
/anime/{page}/{subtype} AnimeGenreQuery (list) AnimeGenreSub
/manga/{page}/{subtype} MangaGenreQuery (list) MangaGenreSub
Producer
/{id}/{page} ProducerQuery Producer
Magazine
/{id}/{page} MagazineQuery Magazine
User
/{username} UserProfileQuery UserProfile
/{username}/history (not implemented)
/{username}/history/anime UserHistoryAnimeQuery (list) UserHistory
/{username}/history/manga UserHistoryAnimeQuery (list) UserHistory
/{username}/friends/{page} UserFriendsQuery (list) UserFriend
/{username}/animelist/{status} UserAnimeQuery (list) UserAnime
/{username}/mangalist/{status} UserMangaQuery (list) UserManga
Club
/{id} ClubQuery Club
/{id}/members/{page} ClubMembersQuery (list) ClubMember
Meta
/status (not implemented)
/requests/{type}/{period}/{page} (not implemented)

reactive-jikan's People

Contributors

fbaierl avatar sandrohc 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

Watchers

 avatar  avatar

reactive-jikan's Issues

NoClassDefFoundError on Android 7.0/7.1

I have encountered an exception that only seems to appear on Android 7.0 and 7.1 devices (at least so far). Not sure how to fix this yet, but I'll just post the Stacktrace here for now:

java.lang.NoClassDefFoundError: 
  at reactor.netty.resources.ConnectionProvider$ConnectionPoolSpec.<init> (ConnectionProvider.java:458)
  at reactor.netty.resources.ConnectionProvider$ConnectionPoolSpec.<init> (ConnectionProvider.java:452)
  at reactor.netty.resources.ConnectionProvider$Builder.<init> (ConnectionProvider.java:404)
  at reactor.netty.resources.ConnectionProvider$Builder.<init> (ConnectionProvider.java:393)
  at reactor.netty.resources.ConnectionProvider.builder (ConnectionProvider.java:103)
  at reactor.netty.tcp.TcpResources.create (TcpResources.java:346)
  at reactor.netty.tcp.TcpResources.getOrCreate (TcpResources.java:290)
  at reactor.netty.http.HttpResources.get (HttpResources.java:42)
  at reactor.netty.http.client.HttpClient.create (HttpClient.java:390)
  at net.sandrohc.jikan.Jikan.<init> (Jikan.java:61)
  at net.sandrohc.jikan.Jikan.<init> (Jikan.java:52)

Btw: thanks a lot for accepting my last PR. It's good to see this lib being maintained since so far I think it's the best when it comes to interacting with the Jikan API from Java. :)

Pagination result

Hello, dont know if you still maintain this lib but I see that the Pagination class lack of information about some query.
Season search got some items informations about the number of items. It would be better to deal with this with a Mono of Paginated Anime to query Jikan from page to page since they have a limit of 25.
Also there were some filter not present on your library about season life sfw or entry type

Anime reviews always returns empty list

After upgrading to v2 of reactive-jikan, the anime reviews query is always returning an empty list. Am I doing something wrong here:

    public List<Review> getAnimeReviews(int malId, int page){
        try {
            final var result = _jikan.query().anime().reviews(malId)
                    .page(page)
                    .execute()
                    .retryWhen(Retry.backoff(BACKOFF_ATTEMPTS, java.time.Duration.ofMillis(BACKOFF_MILLIS)))
                    .collect(Collectors.toList())
                    .doOnError(e -> Log.e(TAG, "Unable to search for reviews for anime with id: " +
                            malId + ". page: " + page + ". " + e.toString()))
                    .onErrorResume(a -> Mono.just(new ArrayList<>()))
                    .block();
            return result;
        } catch (JikanQueryException e) {
            Log.e(TAG, "Unable to search for reviews for anime with id: " +
                    malId + ". page: " + page, e);
            return new ArrayList<>();
        }
    }

I also tried the same mal id as in the test:

image

Plan for ETag / If-None-Match usage

Hello, I'm currently using your lib and would thank you for this amazing job you done.
I also want to know if you plan to implement cache gestion with Etag / If-None-Match Headers ?

Anime themes has type Collection<GenreEntity<AnimeGenre>>

I am not sure if maybe I am misunderstanding something here, but the themes inside the Anime class are defined like this:

	/** The themes. */
	public Collection<GenreEntity<AnimeGenre>> themes = Collections.emptyList();

Isn't this the wrong type?

Deserialization exception for age rating: "R+ - Mild Nudity"

I am currently playing around with your library and I noticed an InvalidFormatException concerning the age rating:

E/JikanWebService: Unable to load anime with id: 40436
    reactor.core.Exceptions$ReactiveException: net.sandrohc.jikan.exception.JikanResponseException: Error parsing JSON for query: net.sandrohc.jikan.query.anime.AnimeQuery
        at reactor.core.Exceptions.propagate(Exceptions.java:393)
        at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:97)
        at reactor.core.publisher.Mono.block(Mono.java:1680)
        at florian.baierl.daily_anime_news.web.JikanWebService.getAnime(JikanWebService.java:43)
        at florian.baierl.daily_anime_news.repository.JikanRepository.loadAnimeFromWeb(JikanRepository.java:38)
        at florian.baierl.daily_anime_news.ui.animeseasons.JikanViewModel.lambda$fetchAnime$2$JikanViewModel(JikanViewModel.java:61)
        at florian.baierl.daily_anime_news.ui.animeseasons.-$$Lambda$JikanViewModel$2oCfGuNrPuSaa5gfN3aWCnc_GmE.call(Unknown Source:4)
        at io.reactivex.rxjava3.internal.operators.single.SingleFromCallable.subscribeActual(SingleFromCallable.java:43)
        at io.reactivex.rxjava3.core.Single.subscribe(Single.java:4813)
        at io.reactivex.rxjava3.internal.operators.single.SingleSubscribeOn$SubscribeOnObserver.run(SingleSubscribeOn.java:89)
        at io.reactivex.rxjava3.core.Scheduler$DisposeTask.run(Scheduler.java:614)
        at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:65)
        at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:56)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:919)
    	Suppressed: java.lang.Exception: #block terminated with an error
        at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:99)
        		... 16 more
     Caused by: net.sandrohc.jikan.exception.JikanResponseException: Error parsing JSON for query: net.sandrohc.jikan.query.anime.AnimeQuery
        at net.sandrohc.jikan.Jikan.dumpStacktrace(Jikan.java:141)
        at net.sandrohc.jikan.query.Query.deserialize(Query.java:43)
        at net.sandrohc.jikan.-$$Lambda$Jchf727RNBPFlMuul1IpRuaem3I.apply(Unknown Source:4)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:118)
        at reactor.core.publisher.SerializedSubscriber.onNext(SerializedSubscriber.java:99)
        at reactor.core.publisher.FluxRetryWhen$RetryWhenMainSubscriber.onNext(FluxRetryWhen.java:162)
        at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1782)
        at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:241)
        at reactor.core.publisher.FluxDoFinally$DoFinallySubscriber.onNext(FluxDoFinally.java:123)
        at reactor.core.publisher.FluxHandle$HandleSubscriber.onNext(FluxHandle.java:112)
        at reactor.core.publisher.FluxMap$MapConditionalSubscriber.onNext(FluxMap.java:213)
        at reactor.core.publisher.FluxDoFinally$DoFinallySubscriber.onNext(FluxDoFinally.java:123)
        at reactor.core.publisher.FluxHandleFuseable$HandleFuseableSubscriber.onNext(FluxHandleFuseable.java:178)
        at reactor.core.publisher.FluxContextStart$ContextStartSubscriber.onNext(FluxContextStart.java:96)
        at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1782)
        at reactor.core.publisher.MonoCollectList$MonoCollectListSubscriber.onComplete(MonoCollectList.java:121)
        at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:252)
        at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:136)
        at reactor.netty.channel.FluxReceive.onInboundComplete(FluxReceive.java:378)
        at reactor.netty.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:373)
        at reactor.netty.channel.ChannelOperations.terminate(ChannelOperations.java:429)
        at reactor.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:625)
E/JikanWebService:     at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:96)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
        at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
        at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324)
        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296)
        at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
        at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1526)
        at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1275)
        at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1322)
        at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:501)
        at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:440)
        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
        at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
        at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
        at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        	... 1 more
     Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `net.sandrohc.jikan.model.enums.AgeRating` from String "R+ - Mild Nudity": not one of the values accepted for Enum class: [R+ - Mild Nudity (may also contain violence & profanity), Rx, G, Rx - Hentai (extreme sexual content/nudity), R, PG-13 - Teens 13 or older, R - 17+ (violence & profanity), PG, PG-13, R - 17+ recommended (violence & profanity), PG - Children, G - All Ages, R+]
        at [Source: (byte[])"{"request_hash":"request:anime:2d20731b3c87c364862b1fe36852cdddc5f9c91a","request_cached":true,"request_cache_expiry":13338,"mal_id":40436,"url":"https:\/\/myanimelist.net\/anime\/40436\/Peter_Grill_to_Kenja_no_Jikan","image_url":"https:\/\/cdn.myanimelist.net\/images\/anime\/1368\/108441.jpg","trailer_url":null,"title":"Peter Grill to Kenja no Jikan","title_english":"Peter Grill and the Philosopher's Time","title_japanese":"\u30d4\u30fc\u30bf\u30fc\u30fb\u30b0\u30ea\u30eb\u3068\u8ce2\u8005\u306"[truncated 1936 bytes]; line: 1, column: 862] (through reference chain: net.sandrohc.jikan.model.anime.Anime["rating"])
        at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:1702)
        at com.fasterxml.jackson.databind.DeserializationContext.handleWeirdStringValue(DeserializationContext.java:947)
        at com.fasterxml.jackson.databind.deser.std.EnumDeserializer._deserializeAltString(EnumDeserializer.java:255)
        at com.fasterxml.jackson.databind.deser.std.EnumDeserializer.deserialize(EnumDeserializer.java:179)
        at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:293)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:156)
        at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4482)
        at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3495)
        at net.sandrohc.jikan.query.Query.deserialize(Query.java:41)
        	... 53 more`

Manga search excluding hentai results in only hentai manga being returned

When calling:

 _jikan.query().manga().search().page(page).query(query)
        .genres(MangaGenre.HENTAI)
        .excludeGivenGenres()
        .execute()
        .retryWhen(Retry.backoff(BACKOFF_ATTEMPTS, java.time.Duration.ofMillis(BACKOFF_MILLIS)))
        .collect(Collectors.toList())
        .doOnError(e -> Log.e(TAG, "Error searching for Manga with query: " + query + "." + e.toString()))
        .onErrorResume(a -> Mono.just(new ArrayList<>()))
        .block();

I get a list of only hentai mangas. Pretty sure the excludeGivenGenres() part is broken.

Migrate to Jikan API V4

The Jikan V3 API is being deprecated starting March 1st, as per this document. Migration to the V4 API has to be finished before September 1st.

This issue will be used to track progress on this topic. All progress is being done on the feature/jikan-v4 branch.

  • Implement V4 endpoints
  • Rewrite unit tests
  • Caching out-of-the-box
  • Simplify query code by extracting response class type from class generics
  • Shims for generic model classes (e.g. create "UserClub" as a wrapper to "Entity")
  • Custom serializers for specialized fields (e.g. anime episode duration)
  • Java 9 modules (will not happen on current release as requires bump to Java 9)

NoSuchMethodError on org.slf4j.Logger.atDebug()

I'm playing with this great 🥇 library inside Spring Boot (Java 17) web app,
but for unknown reason I get this exception :
java.lang.NoSuchMethodError: 'org.slf4j.spi.LoggingEventBuilder org.slf4j.Logger.atDebug()'
I'm using maven latest version : 1.0.2
My intuition is that maven is picking the wrong version of self4j but I'm not familiar with maven exclusions strategies on Pom lavel
Any hint on how to solve this ?

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.