Giter Club home page Giter Club logo

di's Introduction

goioc/di: Dependency Injection

goioc

Go go.dev reference CodeFactor Go Report Card Quality Gate Status codecov DeepSource Awesome

ko-fi

Why DI in Go? Why IoC at all?

I've been using Dependency Injection in Java for nearly 10 years via Spring Framework. I'm not saying that one can't live without it, but it's proven to be very useful for large enterprise-level applications. You may argue that Go follows a completely different ideology, values different principles and paradigms than Java, and DI is not needed in this better world. And I can even partly agree with that. And yet I decided to create this light-weight Spring-like library for Go. You are free to not use it, after all ๐Ÿ™‚

Is it the only DI library for Go?

No, of course not. There's a bunch of libraries around which serve a similar purpose (I even took inspiration from some of them). The problem is that I was missing something in all of these libraries... Therefore I decided to create Yet Another IoC Container that would rule them all. You are more than welcome to use any other library, for example this nice project. And still, I'd recommend stopping by here ๐Ÿ˜‰

So, how does it work?

It's better to show than to describe. Take a look at this toy-example (error-handling is omitted to minimize code snippets):

services/weather_service.go

package services

import (
	"io"
	"net/http"
)

type WeatherService struct {
}

func (ws *WeatherService) Weather(city string) (*string, error) {
	response, err := http.Get("https://wttr.in/" + city)
	if err != nil {
		return nil, err
	}
	all, err := io.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}
	weather := string(all)
	return &weather, nil
}

controllers/weather_controller.go

package controllers

import (
	"di-demo/services"
	"github.com/goioc/di"
	"net/http"
)

type WeatherController struct {
	// note that injection works even with unexported fields
	weatherService *services.WeatherService `di.inject:"weatherService"`
}

func (wc *WeatherController) Weather(w http.ResponseWriter, r *http.Request) {
	weather, _ := wc.weatherService.Weather(r.URL.Query().Get("city"))
	_, _ = w.Write([]byte(*weather))
}

init.go

package main

import (
	"di-demo/controllers"
	"di-demo/services"
	"github.com/goioc/di"
	"reflect"
)

func init() {
	_, _ = di.RegisterBean("weatherService", reflect.TypeOf((*services.WeatherService)(nil)))
	_, _ = di.RegisterBean("weatherController", reflect.TypeOf((*controllers.WeatherController)(nil)))
	_ = di.InitializeContainer()
}

main.go

package main

import (
	"di-demo/controllers"
	"github.com/goioc/di"
	"net/http"
)

func main() {
	http.HandleFunc("/weather", func(w http.ResponseWriter, r *http.Request) {
		di.GetInstance("weatherController").(*controllers.WeatherController).Weather(w, r)
	})
	_ = http.ListenAndServe(":8080", nil)
}

If you run it, you should be able to observe a neat weather forecast at http://localhost:8080/weather?city=London (or for any other city).

Of course, for such a simple example it may look like an overkill. But for larger projects with many interconnected services with complicated business logic, it can really simplify your life!

Looks nice... Give me some details!

The main component of the library is the Inversion of Control Container that contains and manages instances of your structures (called "beans").

Types of beans

  • Singleton. Exists only in one copy in the container. Every time you retrieve the instance from the container (or every time it's being injected to another bean) - it will be the same instance.
  • Prototype. It can exist in multiple copies: a new copy is created upon retrieval from the container (or upon injection into another bean).
  • Request. Similar to Prototype, however it has a few differences and features (since its lifecycle is bound to a web request):
    • Can't be injected to other beans.
    • Can't be manually retrieved from the Container.
    • Request beans are automatically injected to the context.Context of a corresponding http.Request.
    • If a Request bean implements io.Closer, it will be "closed" upon corresponding request's cancellation.

Beans registration

For the container to become aware of the beans, one must register them manually (unlike Java, unfortunately, we can't scan classpath to do it automatically, because Go runtime doesn't contain high-level information about types). How can one register beans in the container?

  • By type. This is described in the example above. A structure is declared with a field tagged with di.scope:"<scope>". This field can be even omitted - in this case, the default scope will be Singleton. Then the registration is done like this:
di.RegisterBean("beanID", reflect.TypeOf((*YourAwesomeStructure)(nil)))
  • Using pre-created instance. What if you already have an instance that you want to register as a bean? You can do it like this:
di.RegisterBeanInstance("beanID", yourAwesomeInstance)

For this type of beans, the only supported scope is Singleton, because I don't dare to clone your instances to enable prototyping ๐Ÿ˜…

  • Via bean factory. If you have a method that is producing instances for you, you can register it as a bean factory:
di.RegisterBeanFactory("beanID", Singleton, func(context.Context) (interface{}, error) {
		return "My awesome string that is going to become a bean!", nil
	})

Feel free to use any scope with this method. By the way, you can even lookup other beans within the factory:

di.RegisterBeanFactory("beanID", Prototype, func(context.Context) (interface{}, error) {
		return di.GetInstance("someOtherBeanID"), nil
	})

Note that factory-method accepts context.Context. It can be useful for request-scoped beans (the HTTP request context is set in this case). For all other beans it will be context.Background().

Beans initialization

There's a special interface InitializingBean that can be implemented to provide your bean with some initialization logic that will be executed after the container is initialized (for Singleton beans) or after the Prototype/Request instance is created. Again, you can also lookup other beans during initialization (since the container is ready by that time):

type PostConstructBean1 struct {
	Value string
}

func (pcb *PostConstructBean1) PostConstruct() error {
	pcb.Value = "some content"
	return nil
}

type PostConstructBean2 struct {
	Scope              Scope `di.scope:"prototype"`
	PostConstructBean1 *PostConstructBean1
}

func (pcb *PostConstructBean2) PostConstruct() error {
	instance, err := di.GetInstanceSafe("postConstructBean1")
	if err != nil {
		return err
	}
	pcb.PostConstructBean1 = instance.(*PostConstructBean1)
	return nil
}

Beans post-processors

The alternative way of initializing beans is using so-called "beans post-processors". Take a look at the example:

type postprocessedBean struct {
	a string
	b string
}

_, _ := RegisterBean("postprocessedBean", reflect.TypeOf((*postprocessedBean)(nil)))

_ = RegisterBeanPostprocessor(reflect.TypeOf((*postprocessedBean)(nil)), func(instance interface{}) error {
    instance.(*postprocessedBean).a = "Hello, "
    return nil
})

_ = RegisterBeanPostprocessor(reflect.TypeOf((*postprocessedBean)(nil)), func(instance interface{}) error {
instance.(*postprocessedBean).b = "world!"
    return nil
})

_ = InitializeContainer()

instance := GetInstance("postprocessedBean")

postprocessedBean := instance.(*postprocessedBean)
println(postprocessedBean.a+postprocessedBean.b) // prints out "Hello, world!"

Beans injection

As was mentioned above, one bean can be injected into another with the PostConstruct method. However, the more handy way of doing it is by using a special tag:

type SingletonBean struct {
	SomeOtherBean *SomeOtherBean `di.inject:"someOtherBean"`
}

... or via interface ...

type SingletonBean struct {
	SomeOtherBean SomeOtherBeansInterface `di.inject:"someOtherBean"`
}

Note that you can refer dependencies either by pointer, or by interface, but not by value. And just a reminder: you can't inject Request beans.

Sometimes we might want to have optional dependencies. By default, all declared dependencies are considered to be required: if some dependency is not found in the Container, you will get an error. However, you can specify an optional dependency like this:

type SingletonBean struct {
	SomeOtherBean *string `di.inject:"someOtherBean" di.optional:"true"`
}

In this case, if someOtherBean is not found in the Container, you will get nil injected into this field.

In fact, you don't need a bean ID to preform an injection! Check this out:

type SingletonBean struct {
	SomeOtherBean *string `di.inject:""`
}

In this case, DI will try to find a candidate for the injection automatically (among registered beans of type *string). Cool, ain't it? ๐Ÿค  It will panic though if no candidates are found (and if the dependency is not marked as optional), or if there is more than one candidate found.

Finally, you can inject beans to slices and maps. It works similarly to the ID-less inections above, but injects all candidates that were found:

type SingletonBean struct {
	someOtherBeans []*string `di.inject:""`
}
type SingletonBean struct {
	someOtherBeans map[string]*string `di.inject:""`
}

Circular dependencies

The problem with all IoC containers is that beans' interconnection may suffer from so-called circular dependencies. Consider this example:

type CircularBean struct {
	Scope        Scope         `di.scope:"prototype"`
	CircularBean *CircularBean `di.inject:"circularBean"`
}

Trying to use such bean will result in the circular dependency detected for bean: circularBean error. There's no problem as such with referencing a bean from itself - if it's a Singleton bean. But doing it with Prototype/Request beans will lead to infinite creation of the instances. So, be careful with this: "with great power comes great responsibility" ๐Ÿ•ธ

What about middleware?

We have some ๐Ÿ˜Ž Here's an example with gorilla/mux router (but feel free to use any other router). Basically, it's an extension of the very first example with the weather controller, but this time we add Request beans and access them via request's context. Also, this example demonstrates how DI can automatically close resources for you (DB connection in this case). The proper error handling is, again, omitted for simplicity.

controllers/weather_controller.go

package controllers

import (
	"database/sql"
	"di-demo/services"
	"github.com/goioc/di"
	"net/http"
)

type WeatherController struct {
	// note that injection works even with unexported fields
	weatherService *services.WeatherService `di.inject:"weatherService"`
}

func (wc *WeatherController) Weather(w http.ResponseWriter, r *http.Request) {
	dbConnection := r.Context().Value(di.BeanKey("dbConnection")).(*sql.Conn)
	city := r.URL.Query().Get("city")
	_, _ = dbConnection.ExecContext(r.Context(), "insert into log values (?, ?, datetime('now'))", city, r.RemoteAddr)
	weather, _ := wc.weatherService.Weather(city)
	_, _ = w.Write([]byte(*weather))
}

controllers/index_controller.go

package controllers

import (
	"database/sql"
	"fmt"
	"github.com/goioc/di"
	"net/http"
	"strings"
	"time"
)

type IndexController struct {
}

func (ic *IndexController) Log(w http.ResponseWriter, r *http.Request) {
	dbConnection := r.Context().Value(di.BeanKey("dbConnection")).(*sql.Conn)
	rows, _ := dbConnection.QueryContext(r.Context(), "select * from log")
	columns, _ := rows.Columns()
	_, _ = w.Write([]byte(strings.ToUpper(fmt.Sprintf("Requests log: %v\n\n", columns))))
	for rows.Next() {
		var city string
		var ip string
		var dateTime time.Time
		_ = rows.Scan(&city, &ip, &dateTime)
		_, _ = w.Write([]byte(fmt.Sprintln(city, "\t", ip, "\t", dateTime)))
	}
}

init.go

package main

import (
	"context"
	"database/sql"
	"di-demo/controllers"
	"di-demo/services"
	"github.com/goioc/di"
	"os"
	"reflect"
)

func init() {
	_, _ = di.RegisterBean("weatherService", reflect.TypeOf((*services.WeatherService)(nil)))
	_, _ = di.RegisterBean("indexController", reflect.TypeOf((*controllers.IndexController)(nil)))
	_, _ = di.RegisterBean("weatherController", reflect.TypeOf((*controllers.WeatherController)(nil)))
	_, _ = di.RegisterBeanFactory("db", di.Singleton, func(context.Context) (interface{}, error) {
		_ = os.Remove("./di-demo.db")
		db, _ := sql.Open("sqlite3", "./di-demo.db")
		db.SetMaxOpenConns(1)
		_, _ = db.Exec("create table log ('city' varchar not null, 'ip' varchar not null, 'time' datetime not null)")
		return db, nil
	})
	_, _ = di.RegisterBeanFactory("dbConnection", di.Request, func(ctx context.Context) (interface{}, error) {
		db, _ := di.GetInstanceSafe("db")
		return db.(*sql.DB).Conn(ctx)
	})
	_ = di.InitializeContainer()
}

main.go

package main

import (
	"di-demo/controllers"
	"github.com/goioc/di"
	"github.com/gorilla/mux"
	_ "github.com/mattn/go-sqlite3"
	"net/http"
)

func main() {
	router := mux.NewRouter()
	router.Use(di.Middleware)
	router.Path("/").HandlerFunc(di.GetInstance("indexController").(*controllers.IndexController).Log)
	router.Path("/weather").Queries("city", "{*?}").HandlerFunc(di.GetInstance("weatherController").(*controllers.WeatherController).Weather)
	_ = http.ListenAndServe(":8080", router)
}

Okaaay... More examples?

Please, take a look at the unit-tests for more examples.

di's People

Contributors

deepsource-autofix[bot] avatar deepsourcebot avatar dependabot-preview[bot] avatar dependabot[bot] avatar dtitov avatar lgtm-migrator avatar marniks7 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  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  avatar  avatar  avatar

di's Issues

[Feature request] Autowiring beans into slice or map depending on the interface

It would be very useful to autowire beans of the specific interface into the []Interface or map[string]Interface.

Spring Java examples:

@Autowired
private Map<String, Interface> services;

@Autowired
private List<Interface> services;

Want to have something like in go:

type Service struct {
     map[string]Interface services `di.mapBeans:"Interface"`
     []Interface services `di.sliceBeans:"Interface"`
}

[Bug report] Can't get another bean instance from bean factory, despite of the docs

Describe the bug
Documentation says:

By the way, you can even lookup other beans within the factory:

di.RegisterBeanFactory("beanID", Singleton, func(context.Context) (interface{}, error) {
	return di.GetInstance("someOtherBeanID"), nil
})

But it fails to initialize a container.

panic: container is not initialized: can't lookup instances of beans yet

goroutine 1 [running]:
github.com/goioc/di.GetInstance(...)
	/tmp/gopath719623559/pkg/mod/github.com/goioc/[email protected]/di.go:499
main.main.func1({0x5a0440?, 0xc000016b40?})
	/tmp/sandbox1767667263/prog.go:16 +0xc7
github.com/goioc/di.createSingletonInstances()
	/tmp/gopath719623559/pkg/mod/github.com/goioc/[email protected]/di.go:414 +0x8cc
github.com/goioc/di.InitializeContainer()
	/tmp/gopath719623559/pkg/mod/github.com/goioc/[email protected]/di.go:104 +0xa5
main.main()
	/tmp/sandbox1767667263/prog.go:22 +0xb1

Program exited.

See example:
https://go.dev/play/p/K-dlscM8hvi

package main

import (
	"context"

	"github.com/goioc/di"
	"github.com/samber/lo"
)

func main() {
	bean1 := "value1"
	_ = lo.Must(di.RegisterBeanInstance("bean1", &bean1))
	_ = lo.Must(
		di.RegisterBeanFactory(
			"bean2", di.Singleton, func(ctx context.Context) (interface{}, error) {
				innerBean1 := di.GetInstance("bean1").(*string)
				bean2 := *innerBean1 + "!"
				return &bean2, nil
			},
		),
	)
	lo.Must0(di.InitializeContainer())
}

Question regarding the implmentation of PostConstruct

Hi, I have a question regarding the implementation of PostConstruct.

func initializeInstance(beanID string, instance interface{}) error {
	initializingBean := reflect.TypeOf((*InitializingBean)(nil)).Elem()
	bean := reflect.TypeOf(instance)
	if bean.Implements(initializingBean) {
		initializingMethod, ok := bean.MethodByName(initializingBean.Method(0).Name)
		if !ok {
			return errors.New("unexpected behavior: can't find method PostConstruct() in bean " + bean.String())
		}
		logrus.WithField("beanID", beanID).Trace("initializing bean")
		errorValue := initializingMethod.Func.Call([]reflect.Value{reflect.ValueOf(instance)})[0]
		if !errorValue.IsNil() {
			return errorValue.Elem().Interface().(error)
		}
	}
	if postprocessors, ok := beanPostprocessors[bean]; ok {
		logrus.WithField("beanID", beanID).Trace("postprocessing bean")
		for _, postprocessor := range postprocessors {
			if err := postprocessor(instance); err != nil {
				return err
			}
		}
	}
	return nil
}

Currently, we would use a serie of reflect technique to convert instance to an implementation of InitializingBean. And then call PostConstruct by errorValue := initializingMethod.Func.Call([]reflect.Value{reflect.ValueOf(instance)})[0].

I don't quite understand the underlying reason for that. Can't we just do it in the following way?

impl, ok := instance.(InitializingBean)
if ok {
    impl.PostConstruct()
}

[Feature request] Bean lifecycle

We rely on bean lifecycle methods and need to have at least bean.destroy() and container.shutdown() methods.

Spring lifecycle is following:
image

Actually, I'm not sure that we need to implement all of these hooks but I believe bean.destroy() and container.shutdown() are really necessary.

[Feature request] Add examples about how to use this library

Is your feature request related to a problem? Please describe.
No, it is a request to add examples as code and as a markdown explaining how to use this library.

Describe the solution you'd like
Code examples, and design architecture suggested by the author.

Describe alternatives you've considered
Without code examples, it would be difficult how to integrate this library with architectures like clean architecture for instance or how to use it in general.

Additional context
None.

Improve beans initialization

Problem
When i read the description, is confusing what i have do for get an initialization. The example is not the best in comparison with weather example.

Solution
Detail that the mentioned interface has the postConstruct method which must be coded so that it is executed after the creation of the instance.

And, thanks for doing this brilliant job!

Been Injection Not Working

Describe the bug
I am trying to use Been Injection using interfaces but it seems not working
To Reproduce
I have the following controller

type AccountController struct {
	accountService services.IAccountService `di.inject:"accountService"`
}

func (controller *AccountController) CreateAccount(ctx *gin.Context) {
	var body commands.CreateAccountCommand
	ctx.ShouldBindBodyWith(&body, binding.JSON)
	utils.Logger.Info("account service is", body)
	result := controller.accountService.CreateAccount(&body)
	if result.Err != nil {
		ctx.Error(result.Err)
		return
	}
	ctx.JSON(http.StatusOK, gin.H{
		"data": result.Data,
	})
}

which injects account service

Here is the interface

type IAccountService interface {
	CreateAccount(createAccountCommand *commands.CreateAccountCommand) *shared.Result[interface{}]
}

and here is the concrete implementation of the accountServce

type AccountService struct {
	accountRepository types.IAccountRepository `di.inject:"accountRepository"`
}

func (accountService *AccountService) CreateAccount(createAccountCommand *commands.CreateAccountCommand) *shared.Result[interface{}] {
	utils.Logger.Info("Hey I am reached here", nil)
	result := &shared.Result[interface{}]{}

	accountAggregate := aggregate.CreateAccount(
		createAccountCommand.FirstName,
		createAccountCommand.LastName,
		createAccountCommand.UserName,
		createAccountCommand.Email,
		createAccountCommand.Password,
		createAccountCommand.Country,
		createAccountCommand.PlanID,
	)

	if accountAggregate.Err != nil {
		result.Err = accountAggregate.Err
		return result
	}

	if accountAggregate.Validations != nil {
		result.Validations = accountAggregate.Validations
		return result
	}

	accountService.accountRepository.CreateAccount(
		accountAggregate.Data.AccountRootEntity.Id,
		accountAggregate.Data.Plan.Id,
		accountAggregate.Data.Plan.Title,
		accountAggregate.Data.Plan.Description,
		accountAggregate.Data.Plan.Price,
		accountAggregate.Data.Plan.Duration,
		accountAggregate.Data.Plan.Features,
		accountAggregate.Data.Users[0].FirstName,
		accountAggregate.Data.Users[0].LastName,
		accountAggregate.Data.Users[0].Username,
		accountAggregate.Data.Users[0].Email,
		accountAggregate.Data.Users[0].Password,
		string(accountAggregate.Data.Users[0].Role),
		accountAggregate.Data.Users[0].Verified,
		accountAggregate.Data.Users[0].Active,
		accountAggregate.Data.Users[0].VerificationCode,
	)

	return result
}

and here is the registering of accountService been

func DiInit() {
	_, err := di.RegisterBeanFactory("accountService", di.Singleton, func(context.Context) (interface{}, error) {
		utils.Logger.Info("I called here", nil)
		return &services.AccountService{}, nil
	})
	if err != nil {
		utils.Logger.Error("failed to inject account service", err, nil)
	}
	utils.Logger.Info("account service injected successfully", nil)
}

when I try to consume the API it gives me the following error:

runtime error: invalid memory address or nil pointer dereference

this happens when trying to access accountService that is injected inside the accountController

Expected behavior
The been to be injected and be able to access it

Desktop (please complete the following information):

  • OS: Ubuntu
  • Version: 2022

Fail to inject dependencies

Hi @dtitov ,
I'm trying to use your package but I have an issue on the initialize part no one of my dependency are injected.
I think misunderstand something but I don't find the issue maybe you can explain me what I miss.

I'm trying to do:

                     |---> RepoA -|
db client ---> |---> RepoB -| ----> ServiceA
                     |---> RepoC -|

I'm creating a db client and injecting it in different repo (responsible of the data) and then these repo will be injecting in one or many services (responsible of the business logic)

I share you some extract of my golang code.

Some struct when I'm trying to do DI:

type UserRepository struct {
	CqlClient cql.CqlClient `di.inject:"CqlClient"`
}

type UserService struct {
	UserRepo          *repository.UserRepository          `di.inject:"UserRepository"`
	CommunicationRepo *repository.CommunicationRepository `di.inject:"CommunicationRepository"`
	AuthRepo          *repository.AuthRepository          `di.inject:"AuthRepository"`
}

Registration of my beans:

repositories := map[string]interface{}{
	"UserRepository":          &repository.UserRepository{},
	"CommunicationRepository": &repository.CommunicationRepository{},
	"AuthRepository":          &repository.AuthRepository{},
}

 // skip code of the init of cqlClient

_, err = di.RegisterBeanInstance("CqlClient", cqlClient)
if err != nil {
	l.Fatalw(context.Background(), err.Error())
}

for k, v := range repositories {
	_, err = di.RegisterBeanInstance(k, v)
	if err != nil {
		l.Fatalw(context.Background(), err.Error())
	}
}

I'm calling the function InitializeContainer

err = di.InitializeContainer()
if err != nil {
	l.Fatalw(context.Background(), err.Error())
}
defer di.Close()

the di.Close() is called only when my server is shutdown.

Some prints where I can see the injection is not done:

fmt.Println(di.GetBeanScopes())
map[AddressService:singleton AuthRepository:singleton CommunicationRepository:singleton CqlClient:singleton UserRepository:singleton UserService:singleton]
fmt.Println(di.GetBeanTypes())
map[AddressService:*address.AddressService AuthRepository:*repository.AuthRepository CommunicationRepository:*repository.CommunicationRepository CqlClient:*cql.CqlManager UserRepository:*repository.UserRepository UserService:*user.UserService]
fmt.Println(di.GetInstance("CqlClient"))
&{<nil> 0xc0003ec380 0xc00011c008 [x-user-uuid x-app-platform x-app-version x-app-build x-request-id uber-trace-id] 0xc0000ae588 <nil> []}
fmt.Println(di.GetInstance("UserRepository"))
&{<nil>}
fmt.Println(di.GetInstance("UserService"))
&{<nil> <nil> <nil>}

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.